Compare commits
8 Commits
master
...
inertia-vu
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d51ea1501 | |||
| 3983cdd554 | |||
| 7e311fbe27 | |||
| 9a4800f6c2 | |||
| 5a49d3c09a | |||
| 6895316fbb | |||
| 2ed84c2b6a | |||
| 4cb4859fe3 |
81
.env.example
81
.env.example
@@ -1,40 +1,59 @@
|
||||
APP_NAME=SkunkLounge
|
||||
APP_NAME=Skunklounge
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:EwYAjTUH78N13Kil0jkqQlxHJSsEJrc+DJzUrHa31GY=
|
||||
APP_KEY=base64:kBh8DxjSOr8EnVoa1Ge5w9nfsLDpkXjZcsop0HtxpEk=
|
||||
APP_DEBUG=true
|
||||
APP_LOG_LEVEL=debug
|
||||
APP_URL=http://localhost:8000
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=skunk_lounge_db
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=secretpassword # Change to a strong password
|
||||
DB_CONNECTION=sqlite
|
||||
#DB_CONNECTION=pgsql
|
||||
#DB_HOST=db
|
||||
#DB_PORT=5432
|
||||
#DB_DATABASE=skunk_lounge
|
||||
#DB_USERNAME=skunk_user
|
||||
#DB_PASSWORD=skunk_pass
|
||||
|
||||
BROADCAST_DRIVER=pusher
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
# CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailpit
|
||||
MAIL_PORT=1025
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
@@ -44,17 +63,19 @@ AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=your_pusher_id # Get from pusher.com
|
||||
PUSHER_APP_KEY=your_key
|
||||
PUSHER_APP_SECRET=your_secret
|
||||
PUSHER_HOST=null
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
|
||||
BROADCAST_DRIVER=pusher
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
PUSHER_APP_ID=your_pusher_id
|
||||
PUSHER_APP_KEY=your_pusher_key
|
||||
PUSHER_APP_SECRET=your_pusher_secret
|
||||
PUSHER_HOST=cluster_host_from_pusher
|
||||
PUSHER_PORT=443
|
||||
PUSHER_SCHEME=https
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
PUSHER_APP_CLUSTER=your_cluster
|
||||
47
Dockerfile
47
Dockerfile
@@ -1,18 +1,35 @@
|
||||
FROM php:8.3-apache
|
||||
RUN apt-get update && apt-get install -y libpq-dev libzip-dev unzip libpng-dev libjpeg-dev libfreetype6-dev libicu-dev && \
|
||||
docker-php-ext-install pdo_pgsql pgsql zip bcmath gd intl
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
npm install -g npm@latest
|
||||
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
|
||||
php composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
|
||||
rm composer-setup.php
|
||||
FROM php:8.3-fpm
|
||||
|
||||
# Install system dependencies and PHP extensions
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libpq-dev \
|
||||
zip unzip \
|
||||
git \
|
||||
nodejs \
|
||||
npm \
|
||||
libicu-dev \
|
||||
libzip-dev \
|
||||
&& docker-php-ext-configure intl \
|
||||
&& docker-php-ext-install pdo pdo_pgsql intl zip \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Copy project files
|
||||
COPY . .
|
||||
RUN composer install --no-dev --optimize-autoloader
|
||||
RUN npm install && npm run build
|
||||
COPY apache.conf /etc/apache2/sites-available/000-default.conf
|
||||
RUN a2enmod rewrite
|
||||
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
|
||||
|
||||
# Set permissions and Git safe directory
|
||||
RUN chown -R www-data:www-data /var/www/html \
|
||||
&& git config --global --add safe.directory /var/www/html
|
||||
|
||||
# Install dependencies
|
||||
RUN composer install --no-dev --optimize-autoloader \
|
||||
&& npm install \
|
||||
&& npm run build
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["apache2-foreground"]
|
||||
CMD ["php-fpm"]
|
||||
98
README.md
98
README.md
@@ -1,65 +1,61 @@
|
||||
# Skunk Lounge - Self-Hosted Fake Casino
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
Skunk Lounge is a self-hosted, fake casino website for personal entertainment, mimicking the look and feel of real casinos like XXX. It uses fake currency only—no real money or payments. Built with Laravel for the backend, Filament for the admin panel, and Docker for easy deployment.
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## Features
|
||||
- **Casino Lobby**: Game selection grid with thumbnails (slots, plinko, wheel, card games from open-source repos).
|
||||
- **User Accounts**: Signup/login, balance management, promo codes for fake deposits.
|
||||
- **Admin Interface**: Manage users, balances, promos, games (odds, bets), stats (wins/losses with charts).
|
||||
- **Games**: Integration-ready for open-source GitHub repos (e.g., Plinko with matter.js, slots, card games from Diablo-Lounge).
|
||||
- **Chat**: Real-time lobby and per-game chat using Socket.io or Pusher.
|
||||
- **Security**: Server-side bet calculations, hashing for integrity, audit logs.
|
||||
- **Database**: PostgreSQL for users, transactions, promos, games.
|
||||
- **Containers**: Docker for app and Postgres.
|
||||
## About Laravel
|
||||
|
||||
## Prerequisites
|
||||
- Docker and Docker Compose.
|
||||
- PHP 8.3+, Composer, Node.js (for local dev).
|
||||
- Gitea for repo (optional).
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
## Setup
|
||||
1. **Clone Repo**:
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
```
|
||||
git clone https://git.skunkhotel.com/arriej/Skunk-Lounge.git
|
||||
cd Skunk-Lounge
|
||||
```
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
2. **Configure .env**:
|
||||
- Copy `.env.example` to `.env`.
|
||||
- Update DB credentials, Pusher keys, secret key (run `docker compose exec app php artisan key:generate`).
|
||||
## Learning Laravel
|
||||
|
||||
3. **Build and Run Containers**:
|
||||
```
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
docker compose exec app php artisan key:generate
|
||||
docker compose exec app php artisan migrate --seed
|
||||
```
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
4. **Access**:
|
||||
- App: http://localhost:8000
|
||||
- Admin: http://localhost:8000/admin (register first account for admin role).
|
||||
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
||||
|
||||
## Usage
|
||||
- **User Side**: Register, use promo codes (e.g., START100), select games from lobby, bet fake $, chat.
|
||||
- **Admin Side**: Manage users/balances, games/odds, promos, view stats/logs.
|
||||
- **Adding Games**: Copy GitHub repo code/assets to `public/games`, hook to /api/bet.
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Development
|
||||
- **Edit Files**: Use Code Server/VS Code.
|
||||
- **Commit to Gitea**: `git add . && git commit -m "Message" && git push`.
|
||||
- **Build Containers with Komodo**: Use Komodo's Docker tools or CLI: `docker compose build`.
|
||||
- **Local Testing**: `docker compose exec app php artisan serve --host=0.0.0.0` (access via port 8000).
|
||||
- **Extensions**: Add PHP extensions in Dockerfile if needed.
|
||||
## Laravel Sponsors
|
||||
|
||||
## Clean Up and Maintenance
|
||||
- Remove unused Laravel files (e.g., default tests).
|
||||
- Update README with custom features.
|
||||
- Security: All bets server-side; add CSRF, rate-limiting as needed.
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Redberry](https://redberry.international/laravel-development)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
## Disclaimer
|
||||
For entertainment only—no real money. Use responsibly.
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<VirtualHost *:80>
|
||||
DocumentRoot /var/www/html/public
|
||||
<Directory /var/www/html/public>
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Broadcasting\Channel;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PresenceChannel;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ChatMessage
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the channels the event should broadcast on.
|
||||
*
|
||||
* @return array<int, \Illuminate\Broadcasting\Channel>
|
||||
*/
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
return [
|
||||
new PrivateChannel('channel-name'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class AboutUs extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-information-circle';
|
||||
|
||||
protected static string $view = 'filament.user.pages.about-us';
|
||||
|
||||
protected static ?int $navigationSort = 6; // Bottom
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class Deposits extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-currency-dollar';
|
||||
|
||||
protected static string $view = 'filament.user.pages.deposits';
|
||||
|
||||
protected static ?int $navigationSort = 2;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Pages;
|
||||
|
||||
use Filament\Pages\Dashboard;
|
||||
|
||||
class Lobby extends Dashboard
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-home';
|
||||
|
||||
protected static string $view = 'filament.user.pages.lobby';
|
||||
|
||||
protected static ?int $navigationSort = 1; // Top
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class Profile extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-user-circle';
|
||||
|
||||
protected static string $view = 'filament.user.pages.profile';
|
||||
|
||||
protected static ?int $navigationSort = 4;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class Transactions extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-document-text';
|
||||
|
||||
protected static string $view = 'filament.user.pages.transactions';
|
||||
|
||||
protected static ?int $navigationSort = 3;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Pages;
|
||||
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class VIP extends Page
|
||||
{
|
||||
protected static ?string $navigationIcon = 'heroicon-o-star';
|
||||
|
||||
protected static string $view = 'filament.user.pages.v-i-p';
|
||||
|
||||
protected static ?int $navigationSort = 5;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\User\Widgets;
|
||||
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class AdminButtonWidget extends Widget
|
||||
{
|
||||
protected static ?int $sort = 1;
|
||||
|
||||
protected static string $view = 'filament.user.widgets.admin-button-widget';
|
||||
|
||||
public function shouldRender(): bool
|
||||
{
|
||||
return auth()->user()->role === 'admin';
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class SecurityHelper
|
||||
{
|
||||
public static function generateBetHash($userId, $amount, $gameId)
|
||||
{
|
||||
return Hash::make($userId . $amount . $gameId . now()->timestamp);
|
||||
}
|
||||
}
|
||||
110
app/Helpers/WalletHelper.php
Normal file
110
app/Helpers/WalletHelper.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Wallet;
|
||||
use App\Models\Transaction;
|
||||
use App\Models\AuditLog;
|
||||
|
||||
class WalletHelper
|
||||
{
|
||||
/**
|
||||
* Process a wallet transaction (e.g., deposit, bet) with atomic updates and audit logging.
|
||||
*
|
||||
* @param int $userId
|
||||
* @param float $amount
|
||||
* @param string $type (deposit, bet, win, withdrawal)
|
||||
* @param string|null $description
|
||||
* @param string|null $ipAddress
|
||||
* @return Transaction
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function processTransaction($userId, $amount, $type, $description = null, $ipAddress = null)
|
||||
{
|
||||
return DB::transaction(function () use ($userId, $amount, $type, $description, $ipAddress) {
|
||||
// Lock wallet for atomic update
|
||||
$wallet = Wallet::where('user_id', $userId)->lockForUpdate()->firstOrFail();
|
||||
|
||||
// Validate balance for bets/withdrawals
|
||||
if (in_array($type, ['bet', 'withdrawal']) && $wallet->balance < $amount) {
|
||||
throw new \Exception('Insufficient Skunk Coins');
|
||||
}
|
||||
|
||||
// Update balance
|
||||
$newBalance = in_array($type, ['bet', 'withdrawal']) ? $wallet->balance - $amount : $wallet->balance + $amount;
|
||||
$wallet->update(['balance' => $newBalance]);
|
||||
|
||||
// Create transaction with SHA-256 hash
|
||||
$hash = hash('sha256', $userId . $amount . $type . time());
|
||||
$transaction = Transaction::create([
|
||||
'wallet_id' => $wallet->id,
|
||||
'amount' => $amount,
|
||||
'type' => $type,
|
||||
'description' => $description,
|
||||
'hash' => $hash,
|
||||
]);
|
||||
|
||||
// Log action in audit_logs
|
||||
AuditLog::create([
|
||||
'user_id' => $userId,
|
||||
'action' => 'wallet_' . $type,
|
||||
'details' => json_encode([
|
||||
'wallet_id' => $wallet->id,
|
||||
'amount' => $amount,
|
||||
'new_balance' => $newBalance,
|
||||
'transaction_id' => $transaction->id,
|
||||
]),
|
||||
'ip_address' => $ipAddress,
|
||||
]);
|
||||
|
||||
return $transaction;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Redeem a promo code for Skunk Coins.
|
||||
*
|
||||
* @param int $userId
|
||||
* @param string $code
|
||||
* @param string|null $ipAddress
|
||||
* @return Transaction
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function redeemPromoCode($userId, $code, $ipAddress = null)
|
||||
{
|
||||
return DB::transaction(function () use ($userId, $code, $ipAddress) {
|
||||
$promo = \App\Models\PromoCode::where('code', $code)
|
||||
->where('is_active', true)
|
||||
->where(function ($query) {
|
||||
$query->where('uses_remaining', -1)->orWhere('uses_remaining', '>', 0);
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereNull('expires_at')->orWhere('expires_at', '>', now());
|
||||
})
|
||||
->firstOrFail();
|
||||
|
||||
// Check if already redeemed
|
||||
if (\App\Models\PromoCodeRedemption::where('user_id', $userId)->where('promo_code_id', $promo->id)->exists()) {
|
||||
throw new \Exception('Promo code already redeemed');
|
||||
}
|
||||
|
||||
// Process deposit
|
||||
$transaction = self::processTransaction($userId, $promo->value, 'deposit', "Promo code: $code", $ipAddress);
|
||||
|
||||
// Record redemption
|
||||
\App\Models\PromoCodeRedemption::create([
|
||||
'promo_code_id' => $promo->id,
|
||||
'user_id' => $userId,
|
||||
'redeemed_at' => now(),
|
||||
]);
|
||||
|
||||
// Decrement uses if limited
|
||||
if ($promo->uses_remaining !== -1) {
|
||||
$promo->decrement('uses_remaining');
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\SecurityHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Game;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BetController extends Controller
|
||||
{
|
||||
public function placeBet(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'game_id' => 'required|exists:games,id',
|
||||
'amount' => 'required|numeric|min:0.01',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$user = auth()->user();
|
||||
if ($user->balance < $validated['amount']) {
|
||||
abort(400, 'Insufficient balance');
|
||||
}
|
||||
|
||||
$hash = SecurityHelper::generateBetHash($user->id, $validated['amount'], $validated['game_id']);
|
||||
$game = Game::find($validated['game_id']);
|
||||
$win = random_int(0, 100) < $game->win_probability;
|
||||
$payout = $win ? $validated['amount'] * 2 : -$validated['amount'];
|
||||
|
||||
$user->balance += $payout;
|
||||
$user->save();
|
||||
|
||||
Transaction::create([
|
||||
'user_id' => $user->id,
|
||||
'type' => $win ? 'win' : 'loss',
|
||||
'amount' => abs($payout),
|
||||
'description' => "Bet hash: $hash",
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'win' => $win,
|
||||
'balance' => $user->balance,
|
||||
'hash' => $hash,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollback();
|
||||
abort(500, $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AuthenticatedSessionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the login view.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming authentication request.
|
||||
*/
|
||||
public function store(LoginRequest $request): RedirectResponse
|
||||
{
|
||||
$request->authenticate();
|
||||
|
||||
$request->session()->regenerate();
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an authenticated session.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
Auth::guard('web')->logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ConfirmablePasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the confirm password view.
|
||||
*/
|
||||
public function show(): View
|
||||
{
|
||||
return view('auth.confirm-password');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the user's password.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
if (! Auth::guard('web')->validate([
|
||||
'email' => $request->user()->email,
|
||||
'password' => $request->password,
|
||||
])) {
|
||||
throw ValidationException::withMessages([
|
||||
'password' => __('auth.password'),
|
||||
]);
|
||||
}
|
||||
|
||||
$request->session()->put('auth.password_confirmed_at', time());
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EmailVerificationNotificationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Send a new email verification notification.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
|
||||
$request->user()->sendEmailVerificationNotification();
|
||||
|
||||
return back()->with('status', 'verification-link-sent');
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class EmailVerificationPromptController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the email verification prompt.
|
||||
*/
|
||||
public function __invoke(Request $request): RedirectResponse|View
|
||||
{
|
||||
return $request->user()->hasVerifiedEmail()
|
||||
? redirect()->intended(route('dashboard', absolute: false))
|
||||
: view('auth.verify-email');
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class NewPasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the password reset view.
|
||||
*/
|
||||
public function create(Request $request): View
|
||||
{
|
||||
return view('auth.reset-password', ['request' => $request]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming new password request.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'token' => ['required'],
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
// Here we will attempt to reset the user's password. If it is successful we
|
||||
// will update the password on an actual user model and persist it to the
|
||||
// database. Otherwise we will parse the error and return the response.
|
||||
$status = Password::reset(
|
||||
$request->only('email', 'password', 'password_confirmation', 'token'),
|
||||
function (User $user) use ($request) {
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($request->password),
|
||||
'remember_token' => Str::random(60),
|
||||
])->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
);
|
||||
|
||||
// If the password was successfully reset, we will redirect the user back to
|
||||
// the application's home authenticated view. If there is an error we can
|
||||
// redirect them back to where they came from with their error message.
|
||||
return $status == Password::PASSWORD_RESET
|
||||
? redirect()->route('login')->with('status', __($status))
|
||||
: back()->withInput($request->only('email'))
|
||||
->withErrors(['email' => __($status)]);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Update the user's password.
|
||||
*/
|
||||
public function update(Request $request): RedirectResponse
|
||||
{
|
||||
$validated = $request->validateWithBag('updatePassword', [
|
||||
'current_password' => ['required', 'current_password'],
|
||||
'password' => ['required', Password::defaults(), 'confirmed'],
|
||||
]);
|
||||
|
||||
$request->user()->update([
|
||||
'password' => Hash::make($validated['password']),
|
||||
]);
|
||||
|
||||
return back()->with('status', 'password-updated');
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PasswordResetLinkController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the password reset link request view.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('auth.forgot-password');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming password reset link request.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'email' => ['required', 'email'],
|
||||
]);
|
||||
|
||||
// We will send the password reset link to this user. Once we have attempted
|
||||
// to send the link, we will examine the response then see the message we
|
||||
// need to show to the user. Finally, we'll send out a proper response.
|
||||
$status = Password::sendResetLink(
|
||||
$request->only('email')
|
||||
);
|
||||
|
||||
return $status == Password::RESET_LINK_SENT
|
||||
? back()->with('status', __($status))
|
||||
: back()->withInput($request->only('email'))
|
||||
->withErrors(['email' => __($status)]);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RegisteredUserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the registration view.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
return view('auth.register');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming registration request.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return redirect(route('dashboard', absolute: false));
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class VerifyEmailController extends Controller
|
||||
{
|
||||
/**
|
||||
* Mark the authenticated user's email address as verified.
|
||||
*/
|
||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
|
||||
if ($request->user()->markEmailAsVerified()) {
|
||||
event(new Verified($request->user()));
|
||||
}
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
}
|
||||
71
app/Http/Controllers/LobbyController.php
Normal file
71
app/Http/Controllers/LobbyController.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Inertia\Inertia;
|
||||
use App\Models\Wallet;
|
||||
use App\Models\Game;
|
||||
use App\Helpers\WalletHelper;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LobbyController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = auth()->user();
|
||||
return Inertia::render('Lobby', [
|
||||
'wallet' => Wallet::where('user_id', $user->id)->first() ?? ['balance' => 0],
|
||||
'games' => Game::where('is_active', true)->get(),
|
||||
'user' => [
|
||||
'name' => $user->name,
|
||||
'vip_level' => $user->vip_level,
|
||||
'is_kyc_verified' => $user->is_kyc_verified,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function addCoins(Request $request)
|
||||
{
|
||||
$request->validate(['amount' => 'required|numeric|min:1']);
|
||||
WalletHelper::processTransaction(auth()->id(), $request->amount, 'deposit', 'Manual coin addition', $request->ip());
|
||||
return Inertia::render('Lobby', [
|
||||
'wallet' => Wallet::where('user_id', auth()->id())->first(),
|
||||
'games' => Game::where('is_active', true)->get(),
|
||||
'user' => [
|
||||
'name' => auth()->user()->name,
|
||||
'vip_level' => auth()->user()->vip_level,
|
||||
'is_kyc_verified' => auth()->user()->is_kyc_verified,
|
||||
],
|
||||
'success' => 'Added ' . $request->amount . ' Skunk Coins',
|
||||
]);
|
||||
}
|
||||
|
||||
public function redeemPromo(Request $request)
|
||||
{
|
||||
$request->validate(['code' => 'required|string']);
|
||||
try {
|
||||
WalletHelper::redeemPromoCode(auth()->id(), $request->code, $request->ip());
|
||||
return Inertia::render('Lobby', [
|
||||
'wallet' => Wallet::where('user_id', auth()->id())->first(),
|
||||
'games' => Game::where('is_active', true)->get(),
|
||||
'user' => [
|
||||
'name' => auth()->user()->name,
|
||||
'vip_level' => auth()->user()->vip_level,
|
||||
'is_kyc_verified' => auth()->user()->is_kyc_verified,
|
||||
],
|
||||
'success' => 'Promo code redeemed successfully',
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return Inertia::render('Lobby', [
|
||||
'wallet' => Wallet::where('user_id', auth()->id())->first(),
|
||||
'games' => Game::where('is_active', true)->get(),
|
||||
'user' => [
|
||||
'name' => auth()->user()->name,
|
||||
'vip_level' => auth()->user()->vip_level,
|
||||
'is_kyc_verified' => auth()->user()->is_kyc_verified,
|
||||
],
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the user's profile form.
|
||||
*/
|
||||
public function edit(Request $request): View
|
||||
{
|
||||
return view('profile.edit', [
|
||||
'user' => $request->user(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's profile information.
|
||||
*/
|
||||
public function update(ProfileUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$request->user()->fill($request->validated());
|
||||
|
||||
if ($request->user()->isDirty('email')) {
|
||||
$request->user()->email_verified_at = null;
|
||||
}
|
||||
|
||||
$request->user()->save();
|
||||
|
||||
return Redirect::route('profile.edit')->with('status', 'profile-updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the user's account.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validateWithBag('userDeletion', [
|
||||
'password' => ['required', 'current_password'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$user->delete();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return Redirect::to('/');
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'string', 'email'],
|
||||
'password' => ['required', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the request's credentials.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function authenticate(): void
|
||||
{
|
||||
$this->ensureIsNotRateLimited();
|
||||
|
||||
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.failed'),
|
||||
]);
|
||||
}
|
||||
|
||||
RateLimiter::clear($this->throttleKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the login request is not rate limited.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function ensureIsNotRateLimited(): void
|
||||
{
|
||||
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event(new Lockout($this));
|
||||
|
||||
$seconds = RateLimiter::availableIn($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.throttle', [
|
||||
'seconds' => $seconds,
|
||||
'minutes' => ceil($seconds / 60),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rate limiting throttle key for the request.
|
||||
*/
|
||||
public function throttleKey(): string
|
||||
{
|
||||
return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ProfileUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'lowercase',
|
||||
'email',
|
||||
'max:255',
|
||||
Rule::unique(User::class)->ignore($this->user()->id),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
42
app/Models/AuditLog.php
Normal file
42
app/Models/AuditLog.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class AuditLog extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'action',
|
||||
'details',
|
||||
'ip_address',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'details' => 'json',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user that owns the audit log.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
33
app/Models/Game.php
Normal file
33
app/Models/Game.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Game extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'slug',
|
||||
'thumbnail_url',
|
||||
'category',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the player stats for the game.
|
||||
*/
|
||||
public function playerStats(): HasMany
|
||||
{
|
||||
return $this->hasMany(PlayerStats::class);
|
||||
}
|
||||
}
|
||||
70
app/Models/PlayerStats.php
Normal file
70
app/Models/PlayerStats.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PlayerStats extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'game_id',
|
||||
'total_winnings',
|
||||
'total_bets',
|
||||
'play_count',
|
||||
'daily_play_amount',
|
||||
'win_streak',
|
||||
'last_played_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'last_played_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user that owns the stats.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the game for the stats (if per-game).
|
||||
*/
|
||||
public function game(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Game::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom method to update stats after a game play (example).
|
||||
*
|
||||
* @param float $betAmount
|
||||
* @param float $winAmount
|
||||
*/
|
||||
public function updateAfterPlay(float $betAmount, float $winAmount): void
|
||||
{
|
||||
$this->increment('play_count');
|
||||
$this->total_bets += $betAmount;
|
||||
$this->total_winnings += $winAmount;
|
||||
$this->daily_play_amount += $betAmount; // Simplify; use cron for daily reset
|
||||
$this->win_streak = $winAmount > 0 ? $this->win_streak + 1 : 0;
|
||||
$this->last_played_at = now();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
42
app/Models/PromoCode.php
Normal file
42
app/Models/PromoCode.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class PromoCode extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'code',
|
||||
'value',
|
||||
'uses_remaining',
|
||||
'expires_at',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the redemptions for the promo code.
|
||||
*/
|
||||
public function redemptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(PromoCodeRedemption::class);
|
||||
}
|
||||
}
|
||||
48
app/Models/PromoCodeRedemption.php
Normal file
48
app/Models/PromoCodeRedemption.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PromoCodeRedemption extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'promo_code_id',
|
||||
'user_id',
|
||||
'redeemed_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'redeemed_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the promo code that owns the redemption.
|
||||
*/
|
||||
public function promoCode(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PromoCode::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that owns the redemption.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
33
app/Models/Transaction.php
Normal file
33
app/Models/Transaction.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Transaction extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'wallet_id',
|
||||
'amount',
|
||||
'type',
|
||||
'description',
|
||||
'hash',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the wallet that owns the transaction.
|
||||
*/
|
||||
public function wallet(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Wallet::class);
|
||||
}
|
||||
}
|
||||
49
app/Models/Wallet.php
Normal file
49
app/Models/Wallet.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Wallet extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'balance',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user that owns the wallet.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transactions for the wallet.
|
||||
*/
|
||||
public function transactions(): HasMany
|
||||
{
|
||||
return $this->hasMany(Transaction::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom method to get total winnings (derived from transactions).
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getTotalWinningsAttribute(): float
|
||||
{
|
||||
return $this->transactions()->where('type', 'win')->sum('amount');
|
||||
}
|
||||
}
|
||||
@@ -3,27 +3,22 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register()
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function boot()
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Register Spatie permissions
|
||||
app(PermissionRegistrar::class)->setPermissionClass(\Spatie\Permission\Models\Permission::class);
|
||||
|
||||
// Redirect authenticated users to Filament lobby for specific routes
|
||||
$this->app['router']->middleware('web')->group(function () {
|
||||
$currentRoute = request()->route()?->getName();
|
||||
if (Auth::check() && in_array($currentRoute, ['home', 'filament.user.auth.login', 'filament.user.auth.register'])) {
|
||||
redirect()->route('filament.user.pages.lobby')->send();
|
||||
}
|
||||
});
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,26 +17,32 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
use Spatie\Permission\Middlewares\RoleMiddleware;
|
||||
|
||||
class AdminPanelProvider extends PanelProvider
|
||||
{
|
||||
public function panel(Panel $panel): Panel
|
||||
{
|
||||
return $panel
|
||||
->default()
|
||||
->id('admin')
|
||||
->path('admin')
|
||||
->login()
|
||||
->darkMode(true) // Enforce dark theme for casino admin vibe
|
||||
->colors([
|
||||
'primary' => Color::Amber,
|
||||
'primary' => Color::hex('#4CAF50'), // Crypto-green for consistency with lobby
|
||||
'secondary' => Color::hex('#0D1117'), // Dark background to match Stake.com aesthetic
|
||||
])
|
||||
->discoverResources(in: app_path('Filament/Admin/Resources'), for: 'App\\Filament\\Admin\\Resources')
|
||||
->discoverPages(in: app_path('Filament/Admin/Pages'), for: 'App\\Filament\\Admin\\Pages')
|
||||
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||
->pages([
|
||||
Pages\Dashboard::class,
|
||||
])
|
||||
->discoverWidgets(in: app_path('Filament/Admin/Widgets'), for: 'App\\Filament\\Admin\\Widgets')
|
||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
|
||||
->widgets([
|
||||
Widgets\AccountWidget::class,
|
||||
Widgets\FilamentInfoWidget::class,
|
||||
// Add custom widgets later for stats (e.g., user activity, Skunk Coins transactions)
|
||||
])
|
||||
->middleware([
|
||||
EncryptCookies::class,
|
||||
@@ -51,6 +57,10 @@ class AdminPanelProvider extends PanelProvider
|
||||
])
|
||||
->authMiddleware([
|
||||
Authenticate::class,
|
||||
]);
|
||||
RoleMiddleware::class . ':admin', // Restrict to admin role via Spatie
|
||||
])
|
||||
->brandName('Skunk Lounge Admin')
|
||||
->brandLogo(asset('images/logo.png')) // Placeholder for custom logo
|
||||
->favicon(asset('images/favicon.ico')); // Placeholder for favicon
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\AuthenticateSession;
|
||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||
use Filament\Pages;
|
||||
use Filament\Panel;
|
||||
use Filament\PanelProvider;
|
||||
use Filament\Support\Colors\Color;
|
||||
use Filament\Widgets;
|
||||
use Filament\Navigation\NavigationItem;
|
||||
use Filament\Navigation\NavigationGroup;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
|
||||
class UserPanelProvider extends PanelProvider
|
||||
{
|
||||
public function panel(Panel $panel): Panel
|
||||
{
|
||||
return $panel
|
||||
->id('user')
|
||||
->path('lobby')
|
||||
->login()
|
||||
->registration()
|
||||
->colors([
|
||||
'primary' => Color::Purple,
|
||||
])
|
||||
->brandLogo(null)
|
||||
->discoverResources(in: app_path('Filament/User/Resources'), for: 'App\\Filament\\User\\Resources')
|
||||
->discoverPages(in: app_path('Filament/User/Pages'), for: 'App\\Filament\\User\\Pages')
|
||||
->pages([
|
||||
\App\Filament\User\Pages\Lobby::class,
|
||||
])
|
||||
->discoverWidgets(in: app_path('Filament/User/Widgets'), for: 'App\\Filament\\User\\Widgets')
|
||||
->widgets([
|
||||
Widgets\AccountWidget::class,
|
||||
])
|
||||
->middleware([
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
AuthenticateSession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
SubstituteBindings::class,
|
||||
DisableBladeIconComponents::class,
|
||||
DispatchServingFilamentEvent::class,
|
||||
])
|
||||
->authMiddleware([
|
||||
Authenticate::class,
|
||||
])
|
||||
->navigationGroups([
|
||||
NavigationGroup::make('Casino')
|
||||
->items([
|
||||
NavigationItem::make('Lobby')
|
||||
->url(fn () => route('filament.user.pages.lobby'))
|
||||
->icon('heroicon-o-home')
|
||||
->sort(1),
|
||||
NavigationItem::make('Deposits')
|
||||
->url(fn () => route('filament.user.pages.deposits'))
|
||||
->icon('heroicon-o-currency-dollar')
|
||||
->sort(2),
|
||||
NavigationItem::make('Transactions')
|
||||
->url(fn () => route('filament.user.pages.transactions'))
|
||||
->icon('heroicon-o-document-text')
|
||||
->sort(3),
|
||||
NavigationItem::make('Profile')
|
||||
->url(fn () => route('filament.user.pages.profile'))
|
||||
->icon('heroicon-o-user-circle')
|
||||
->sort(4),
|
||||
NavigationItem::make('VIP')
|
||||
->url(fn () => route('filament.user.pages.v-i-p'))
|
||||
->icon('heroicon-o-star')
|
||||
->sort(5),
|
||||
NavigationItem::make('About Us')
|
||||
->url(fn () => route('filament.user.pages.about-us'))
|
||||
->icon('heroicon-o-information-circle')
|
||||
->sort(6),
|
||||
]),
|
||||
NavigationGroup::make('Administration')
|
||||
->items([
|
||||
NavigationItem::make('Admin Panel')
|
||||
->url('/admin')
|
||||
->icon('heroicon-o-shield-check')
|
||||
->visible(fn () => auth()->user() && auth()->user()->role === 'admin')
|
||||
->sort(7),
|
||||
]),
|
||||
])
|
||||
->default();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AppLayout extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('layouts.app');
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class GuestLayout extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('layouts.guest');
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Lobby extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.lobby');
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,4 @@
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\Filament\AdminPanelProvider::class,
|
||||
App\Providers\Filament\UserPanelProvider::class,
|
||||
|
||||
];
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"doctrine/dbal": "^4.3",
|
||||
"filament/filament": "^3.3",
|
||||
"laravel/breeze": "^2.3",
|
||||
"inertiajs/inertia-laravel": "^2.0",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/sanctum": "^4.2",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"livewire/livewire": "^3.6",
|
||||
"pusher/pusher-php-server": "^7.2",
|
||||
"pusher/pusher-php-server": "~7.2",
|
||||
"spatie/laravel-permission": "^6.21"
|
||||
},
|
||||
"require-dev": {
|
||||
|
||||
271
composer.lock
generated
271
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b7822b3bd2227e2c0dee864f6ee7be57",
|
||||
"content-hash": "7c664dab79ad9a7e1e82b353d06abbf1",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -224,25 +224,25 @@
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.13.1",
|
||||
"version": "0.14.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/brick/math.git",
|
||||
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
|
||||
"reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
|
||||
"reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
|
||||
"url": "https://api.github.com/repos/brick/math/zipball/113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
|
||||
"reference": "113a8ee2656b882d4c3164fa31aa6e12cbb7aaa2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"phpunit/phpunit": "^10.1",
|
||||
"vimeo/psalm": "6.8.8"
|
||||
"phpstan/phpstan": "2.1.22",
|
||||
"phpunit/phpunit": "^11.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -272,7 +272,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/brick/math/issues",
|
||||
"source": "https://github.com/brick/math/tree/0.13.1"
|
||||
"source": "https://github.com/brick/math/tree/0.14.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -280,7 +280,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-29T13:50:30+00:00"
|
||||
"time": "2025-08-29T12:40:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "carbonphp/carbon-doctrine-types",
|
||||
@@ -533,16 +533,16 @@
|
||||
},
|
||||
{
|
||||
"name": "doctrine/dbal",
|
||||
"version": "4.3.2",
|
||||
"version": "4.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/dbal.git",
|
||||
"reference": "7669f131d43b880de168b2d2df9687d152d6c762"
|
||||
"reference": "231959669bb2173194c95636eae7f1b41b2a8b19"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/7669f131d43b880de168b2d2df9687d152d6c762",
|
||||
"reference": "7669f131d43b880de168b2d2df9687d152d6c762",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/231959669bb2173194c95636eae7f1b41b2a8b19",
|
||||
"reference": "231959669bb2173194c95636eae7f1b41b2a8b19",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -552,10 +552,10 @@
|
||||
"psr/log": "^1|^2|^3"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "13.0.0",
|
||||
"doctrine/coding-standard": "13.0.1",
|
||||
"fig/log-test": "^1",
|
||||
"jetbrains/phpstorm-stubs": "2023.2",
|
||||
"phpstan/phpstan": "2.1.17",
|
||||
"phpstan/phpstan": "2.1.22",
|
||||
"phpstan/phpstan-phpunit": "2.0.6",
|
||||
"phpstan/phpstan-strict-rules": "^2",
|
||||
"phpunit/phpunit": "11.5.23",
|
||||
@@ -619,7 +619,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/dbal/issues",
|
||||
"source": "https://github.com/doctrine/dbal/tree/4.3.2"
|
||||
"source": "https://github.com/doctrine/dbal/tree/4.3.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -635,7 +635,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-05T13:30:38+00:00"
|
||||
"time": "2025-09-04T23:52:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
@@ -1960,6 +1960,76 @@
|
||||
],
|
||||
"time": "2025-08-22T14:27:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "inertiajs/inertia-laravel",
|
||||
"version": "v2.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/inertiajs/inertia-laravel.git",
|
||||
"reference": "6d0afc3237c370036de4a703927b17a4e7b83298"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/6d0afc3237c370036de4a703927b17a4e7b83298",
|
||||
"reference": "6d0afc3237c370036de4a703927b17a4e7b83298",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"laravel/framework": "^10.0|^11.0|^12.0",
|
||||
"php": "^8.1.0",
|
||||
"symfony/console": "^6.2|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"larastan/larastan": "^3.0",
|
||||
"laravel/pint": "^1.16",
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench": "^8.0|^9.2|^10.0",
|
||||
"phpunit/phpunit": "^10.4|^11.5",
|
||||
"roave/security-advisories": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Inertia\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"./helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Inertia\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jonathan Reinink",
|
||||
"email": "jonathan@reinink.ca",
|
||||
"homepage": "https://reinink.ca"
|
||||
}
|
||||
],
|
||||
"description": "The Laravel adapter for Inertia.js.",
|
||||
"keywords": [
|
||||
"inertia",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
|
||||
"source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.6"
|
||||
},
|
||||
"time": "2025-08-26T06:17:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kirschbaum-development/eloquent-power-joins",
|
||||
"version": "4.2.8",
|
||||
@@ -2023,79 +2093,18 @@
|
||||
},
|
||||
"time": "2025-08-14T18:43:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/breeze",
|
||||
"version": "v2.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/breeze.git",
|
||||
"reference": "1a29c5792818bd4cddf70b5f743a227e02fbcfcd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/breeze/zipball/1a29c5792818bd4cddf70b5f743a227e02fbcfcd",
|
||||
"reference": "1a29c5792818bd4cddf70b5f743a227e02fbcfcd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/console": "^11.0|^12.0",
|
||||
"illuminate/filesystem": "^11.0|^12.0",
|
||||
"illuminate/support": "^11.0|^12.0",
|
||||
"illuminate/validation": "^11.0|^12.0",
|
||||
"php": "^8.2.0",
|
||||
"symfony/console": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/framework": "^11.0|^12.0",
|
||||
"orchestra/testbench-core": "^9.0|^10.0",
|
||||
"phpstan/phpstan": "^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Breeze\\BreezeServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Breeze\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/breeze/issues",
|
||||
"source": "https://github.com/laravel/breeze"
|
||||
},
|
||||
"time": "2025-07-18T18:49:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.27.1",
|
||||
"version": "v12.28.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "168844fe531baaa11db00f7902fd0116d46d3bdd"
|
||||
"reference": "868c1f2d3dba4df6d21e3a8d818479f094cfd942"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/168844fe531baaa11db00f7902fd0116d46d3bdd",
|
||||
"reference": "168844fe531baaa11db00f7902fd0116d46d3bdd",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/868c1f2d3dba4df6d21e3a8d818479f094cfd942",
|
||||
"reference": "868c1f2d3dba4df6d21e3a8d818479f094cfd942",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2173,6 +2182,7 @@
|
||||
"illuminate/filesystem": "self.version",
|
||||
"illuminate/hashing": "self.version",
|
||||
"illuminate/http": "self.version",
|
||||
"illuminate/json-schema": "self.version",
|
||||
"illuminate/log": "self.version",
|
||||
"illuminate/macroable": "self.version",
|
||||
"illuminate/mail": "self.version",
|
||||
@@ -2205,7 +2215,8 @@
|
||||
"league/flysystem-read-only": "^3.25.1",
|
||||
"league/flysystem-sftp-v3": "^3.25.1",
|
||||
"mockery/mockery": "^1.6.10",
|
||||
"orchestra/testbench-core": "^10.6.3",
|
||||
"opis/json-schema": "^2.4.1",
|
||||
"orchestra/testbench-core": "^10.6.5",
|
||||
"pda/pheanstalk": "^5.0.6|^7.0.0",
|
||||
"php-http/discovery": "^1.15",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
@@ -2299,7 +2310,7 @@
|
||||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-09-02T23:59:38+00:00"
|
||||
"time": "2025-09-04T14:58:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
@@ -2360,70 +2371,6 @@
|
||||
},
|
||||
"time": "2025-07-07T14:17:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sanctum",
|
||||
"version": "v4.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/sanctum.git",
|
||||
"reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/sanctum/zipball/fd6df4f79f48a72992e8d29a9c0ee25422a0d677",
|
||||
"reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^11.0|^12.0",
|
||||
"illuminate/contracts": "^11.0|^12.0",
|
||||
"illuminate/database": "^11.0|^12.0",
|
||||
"illuminate/support": "^11.0|^12.0",
|
||||
"php": "^8.2",
|
||||
"symfony/console": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.6",
|
||||
"orchestra/testbench": "^9.0|^10.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^11.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Sanctum\\SanctumServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Sanctum\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"laravel",
|
||||
"sanctum"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/sanctum/issues",
|
||||
"source": "https://github.com/laravel/sanctum"
|
||||
},
|
||||
"time": "2025-07-09T19:45:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.4",
|
||||
@@ -4821,20 +4768,20 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.9.0",
|
||||
"version": "4.9.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0"
|
||||
"reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0",
|
||||
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/81f941f6f729b1e3ceea61d9d014f8b6c6800440",
|
||||
"reference": "81f941f6f729b1e3ceea61d9d014f8b6c6800440",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
|
||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14",
|
||||
"php": "^8.0",
|
||||
"ramsey/collection": "^1.2 || ^2.0"
|
||||
},
|
||||
@@ -4893,9 +4840,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.9.0"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.9.1"
|
||||
},
|
||||
"time": "2025-06-25T14:20:11+00:00"
|
||||
"time": "2025-09-04T20:59:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ryangjchandler/blade-capture-directive",
|
||||
@@ -9154,16 +9101,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "11.5.35",
|
||||
"version": "11.5.36",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "d341ee94ee5007b286fc7907b383aae6b5b3cc91"
|
||||
"reference": "264a87c7ef68b1ab9af7172357740dc266df5957"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d341ee94ee5007b286fc7907b383aae6b5b3cc91",
|
||||
"reference": "d341ee94ee5007b286fc7907b383aae6b5b3cc91",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/264a87c7ef68b1ab9af7172357740dc266df5957",
|
||||
"reference": "264a87c7ef68b1ab9af7172357740dc266df5957",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -9235,7 +9182,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.35"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.36"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9259,7 +9206,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-28T05:13:54+00:00"
|
||||
"time": "2025-09-03T06:24:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
||||
@@ -13,7 +13,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Skunk Lounge'),
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -40,6 +40,7 @@ return [
|
||||
'busy_timeout' => null,
|
||||
'journal_mode' => null,
|
||||
'synchronous' => null,
|
||||
'transaction_mode' => 'DEFERRED',
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
@@ -158,6 +159,10 @@ return [
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
@@ -167,6 +172,10 @@ return [
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?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('role')->default('user');
|
||||
$table->decimal('balance', 10, 2)->default(0.00);
|
||||
$table->enum('account_status', ['active', 'suspended', 'banned'])->default('active');
|
||||
$table->enum('kyc_status', ['pending', 'verified', 'rejected'])->default('pending');
|
||||
$table->string('kyc_document_path')->nullable(); // For future KYC file uploads
|
||||
$table->timestamp('last_login')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn(['role', 'balance', 'account_status', 'kyc_status', 'kyc_document_path', 'last_login']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
<?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('site_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique();
|
||||
$table->string('value');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('site_settings');
|
||||
}
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
<?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('chats', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('room'); // e.g., 'lobby' or 'game_1'
|
||||
$table->foreignId('user_id')->constrained();
|
||||
$table->text('message');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('chats');
|
||||
}
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
<?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('games', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->boolean('enabled')->default(true);
|
||||
$table->decimal('win_probability', 5, 2)->default(50.00);
|
||||
$table->decimal('min_bet', 10, 2)->default(1.00);
|
||||
$table->decimal('max_bet', 10, 2)->default(100.00);
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('games');
|
||||
}
|
||||
};
|
||||
@@ -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('users', function (Blueprint $table) {
|
||||
$table->boolean('is_kyc_verified')->default(false)->comment('KYC-like verification status');
|
||||
$table->integer('vip_level')->default(0)->comment('VIP status: 0 = standard, 1-5 = tiers');
|
||||
$table->timestamp('last_login_at')->nullable()->comment('For tracking activity');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn(['is_kyc_verified', 'vip_level', 'last_login_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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('wallets', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete()->comment('Links to user');
|
||||
$table->decimal('balance', 15, 2)->default(0.00)->comment('Skunk Coins balance');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('wallets');
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('transactions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('wallet_id')->constrained()->cascadeOnDelete()->comment('Links to wallet');
|
||||
$table->decimal('amount', 15, 2)->comment('Amount transacted');
|
||||
$table->enum('type', ['bet', 'win', 'deposit', 'withdrawal'])->comment('Transaction type');
|
||||
$table->text('description')->nullable()->comment('Details, e.g., "Bet on Plinko"');
|
||||
$table->string('hash')->comment('SHA-256 hash for validation');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('transactions');
|
||||
}
|
||||
};
|
||||
26
database/migrations/2025_09_06_093949_create_games_table.php
Normal file
26
database/migrations/2025_09_06_093949_create_games_table.php
Normal file
@@ -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
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('games', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->comment('Game name, e.g., Plinko');
|
||||
$table->string('slug')->unique()->comment('URL-friendly slug');
|
||||
$table->string('thumbnail_url')->nullable()->comment('Vibrant thumbnail for lobby');
|
||||
$table->enum('category', ['slots', 'plinko', 'wheel', 'cards', 'other'])->default('other')->comment('For filtering');
|
||||
$table->boolean('is_active')->default(true)->comment('Toggle for maintenance');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('games');
|
||||
}
|
||||
};
|
||||
@@ -10,11 +10,11 @@ return new class extends Migration
|
||||
{
|
||||
Schema::create('promo_codes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('code')->unique();
|
||||
$table->decimal('value', 10, 2);
|
||||
$table->boolean('used')->default(false);
|
||||
$table->foreignId('user_id')->nullable()->constrained();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->string('code')->unique()->comment('Unique promo code, e.g., WELCOME100');
|
||||
$table->decimal('value', 15, 2)->comment('Skunk Coins awarded');
|
||||
$table->integer('uses_remaining')->default(-1)->comment('-1 for unlimited');
|
||||
$table->timestamp('expires_at')->nullable()->comment('Expiration date');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
@@ -8,18 +8,17 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('transactions', function (Blueprint $table) {
|
||||
Schema::create('promo_code_redemptions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('promo_code_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->enum('type', ['win', 'loss', 'deposit', 'withdraw', 'promo', 'adjustment']);
|
||||
$table->decimal('amount', 10, 2);
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamp('redeemed_at');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('transactions');
|
||||
Schema::dropIfExists('promo_code_redemptions');
|
||||
}
|
||||
};
|
||||
@@ -10,9 +10,10 @@ return new class extends Migration
|
||||
{
|
||||
Schema::create('audit_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained();
|
||||
$table->string('action');
|
||||
$table->json('details')->nullable();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->cascadeOnDelete()->comment('Admin or player who performed action');
|
||||
$table->string('action')->comment('E.g., "updated_user_balance"');
|
||||
$table->json('details')->nullable()->comment('Before/after changes');
|
||||
$table->string('ip_address')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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('player_stats', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('game_id')->nullable()->constrained()->cascadeOnDelete()->comment('Null for overall stats');
|
||||
$table->decimal('total_winnings', 15, 2)->default(0.00);
|
||||
$table->decimal('total_bets', 15, 2)->default(0.00);
|
||||
$table->integer('play_count')->default(0);
|
||||
$table->decimal('daily_play_amount', 15, 2)->default(0.00)->comment('Bets in last 24 hours');
|
||||
$table->integer('win_streak')->default(0);
|
||||
$table->timestamp('last_played_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('player_stats');
|
||||
}
|
||||
};
|
||||
@@ -2,48 +2,54 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Models\User;
|
||||
use App\Models\Wallet;
|
||||
use App\Models\Game;
|
||||
use App\Models\PromoCode;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// Seed admin user (for first-time admin)
|
||||
User::create([
|
||||
'name' => 'Admin',
|
||||
// Roles (Spatie)
|
||||
Role::create(['name' => 'admin']);
|
||||
Role::create(['name' => 'player']);
|
||||
Role::create(['name' => 'vip']);
|
||||
|
||||
// Test User (Player)
|
||||
$player = User::factory()->create([
|
||||
'name' => 'Test Player',
|
||||
'email' => 'player@skunklounge.com',
|
||||
'password' => bcrypt('password'),
|
||||
'is_kyc_verified' => true,
|
||||
'vip_level' => 1,
|
||||
]);
|
||||
$player->assignRole('player', 'vip');
|
||||
|
||||
// Test Admin
|
||||
$admin = User::factory()->create([
|
||||
'name' => 'Test Admin',
|
||||
'email' => 'admin@skunklounge.com',
|
||||
'password' => Hash::make('securepass123'),
|
||||
'role' => 'admin',
|
||||
'balance' => 1000.00,
|
||||
'account_status' => 'active',
|
||||
'kyc_status' => 'verified',
|
||||
'password' => bcrypt('password'),
|
||||
'is_kyc_verified' => true,
|
||||
'vip_level' => 0,
|
||||
]);
|
||||
$admin->assignRole('admin');
|
||||
|
||||
// Seed site settings
|
||||
DB::table('site_settings')->insert([
|
||||
['key' => 'currency_symbol', 'value' => '$'],
|
||||
['key' => 'default_balance', 'value' => '100.00'],
|
||||
['key' => 'maintenance_mode', 'value' => 'false'],
|
||||
]);
|
||||
// Wallets
|
||||
Wallet::create(['user_id' => $player->id, 'balance' => 10000.00]);
|
||||
Wallet::create(['user_id' => $admin->id, 'balance' => 0.00]);
|
||||
|
||||
// Seed promo code
|
||||
DB::table('promo_codes')->insert([
|
||||
'code' => 'START100',
|
||||
'value' => 100.00,
|
||||
]);
|
||||
// Games
|
||||
Game::create(['name' => 'Plinko', 'slug' => 'plinko', 'thumbnail_url' => 'https://placehold.co/300x200', 'category' => 'plinko']);
|
||||
Game::create(['name' => 'Slots', 'slug' => 'slots', 'thumbnail_url' => 'https://placehold.co/300x200', 'category' => 'slots']);
|
||||
|
||||
// Seed sample game
|
||||
DB::table('games')->insert([
|
||||
'name' => 'Slots',
|
||||
'enabled' => true,
|
||||
'win_probability' => 50.00,
|
||||
'min_bet' => 1.00,
|
||||
'max_bet' => 100.00,
|
||||
'description' => 'Pragmatic Play-style slots',
|
||||
]);
|
||||
// Promo Codes
|
||||
PromoCode::create(['code' => 'WELCOME100', 'value' => 100.00, 'uses_remaining' => -1, 'is_active' => true]);
|
||||
|
||||
// Player Stats (initial)
|
||||
\App\Models\PlayerStats::create(['user_id' => $player->id, 'total_winnings' => 0.00, 'total_bets' => 0.00, 'play_count' => 0]);
|
||||
}
|
||||
}
|
||||
17
database/seeders/PlayerStatsSeeder.php
Normal file
17
database/seeders/PlayerStatsSeeder.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class PlayerStatsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,58 @@
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: skunk-lounge-web
|
||||
ports:
|
||||
- "8000:80"
|
||||
# volumes:
|
||||
# - .:/var/www/html
|
||||
depends_on:
|
||||
- postgres
|
||||
env_file:
|
||||
- .env
|
||||
postgres:
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_DB: skunk_lounge_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: secretpassword
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- .:/var/www/html
|
||||
depends_on:
|
||||
- db
|
||||
environment:
|
||||
DB_CONNECTION: pgsql
|
||||
DB_HOST: db
|
||||
DB_PORT: 5432
|
||||
DB_DATABASE: skunk_lounge
|
||||
DB_USERNAME: skunk_user
|
||||
DB_PASSWORD: skunk_pass
|
||||
APP_KEY: ${APP_KEY}
|
||||
networks:
|
||||
- skunk-net
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
container_name: skunk-lounge-db
|
||||
restart: always
|
||||
ports:
|
||||
- "5433:5432"
|
||||
environment:
|
||||
POSTGRES_DB: skunk_lounge
|
||||
POSTGRES_USER: skunk_user
|
||||
POSTGRES_PASSWORD: skunk_pass
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- skunk-net
|
||||
|
||||
vite:
|
||||
image: node:20
|
||||
container_name: skunk-lounge-vite
|
||||
working_dir: /var/www/html
|
||||
volumes:
|
||||
- .:/var/www/html
|
||||
command: sh -c "sleep 5 && npm install && npm run dev"
|
||||
ports:
|
||||
- "5173:5173"
|
||||
depends_on:
|
||||
- web
|
||||
networks:
|
||||
- skunk-net
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
db-data:
|
||||
|
||||
networks:
|
||||
skunk-net:
|
||||
driver: bridge
|
||||
1610
package-lock.json
generated
1610
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -7,17 +7,22 @@
|
||||
"dev": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.2",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"alpinejs": "^3.4.2",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"axios": "^1.11.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"@inertiajs/vue3": "^1.2.0",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.6.4",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^2.0.0",
|
||||
"postcss": "^8.4.31",
|
||||
"tailwindcss": "^3.1.0",
|
||||
"vite": "^7.0.4"
|
||||
"laravel-echo": "^2.2.0",
|
||||
"laravel-vite-plugin": "^1.0",
|
||||
"postcss": "^8.4.47",
|
||||
"pusher-js": "^8.4.0",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"vite": "^5.4.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.4.0",
|
||||
"laravel-echo": "^1.15.0",
|
||||
"pusher-js": "^8.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
.bg-dark {
|
||||
background-color: #111;
|
||||
}
|
||||
.glow {
|
||||
text-shadow: 0 0 10px #00ff00;
|
||||
}
|
||||
.shadow-neon {
|
||||
box-shadow: 0 0 15px #ff00de;
|
||||
}
|
||||
.btn-neon {
|
||||
background-color: #00ff00;
|
||||
color: #111;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 0 10px #00ff00;
|
||||
}
|
||||
.btn-neon:hover {
|
||||
box-shadow: 0 0 15px #00ff00;
|
||||
}
|
||||
@@ -2,23 +2,16 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||
@source '../../storage/framework/views/*.php';
|
||||
@source '../**/*.blade.php';
|
||||
@source '../**/*.js';
|
||||
|
||||
.bg-dark {
|
||||
background-color: #111;
|
||||
@theme {
|
||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
.glow {
|
||||
text-shadow: 0 0 10px #00ff00;
|
||||
}
|
||||
.shadow-neon {
|
||||
box-shadow: 0 0 15px #ff00de;
|
||||
}
|
||||
.btn-neon {
|
||||
background-color: #00ff00;
|
||||
color: #111;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 0 10px #00ff00;
|
||||
}
|
||||
.btn-neon:hover {
|
||||
box-shadow: 0 0 15px #00ff00;
|
||||
|
||||
.game-card:hover {
|
||||
@apply shadow-lg shadow-crypto-green/50;
|
||||
}
|
||||
82
resources/js/Pages/Lobby.vue
Normal file
82
resources/js/Pages/Lobby.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="min-h-screen">
|
||||
<nav class="bg-gray-800 p-4 flex justify-between">
|
||||
<div class="text-2xl font-bold">Skunk Lounge</div>
|
||||
<div class="space-x-4">
|
||||
<span class="text-crypto-green">{{ user.name }} (VIP Level: {{ user.vip_level }})</span>
|
||||
<span v-if="user.is_kyc_verified" class="text-green-400">KYC Verified</span>
|
||||
<a href="/lobby" class="hover:text-crypto-green">Games</a>
|
||||
<a href="/profile" class="hover:text-crypto-green">Profile</a>
|
||||
<a href="/logout" class="hover:text-crypto-green">Logout</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="p-6">
|
||||
<div v-if="success" class="bg-green-500 p-2 rounded mb-4">{{ success }}</div>
|
||||
<div v-if="error" class="bg-red-500 p-2 rounded mb-4">{{ error }}</div>
|
||||
<div class="bg-gray-800 rounded-lg p-4 mb-6 flex justify-between items-center">
|
||||
<span>Skunk Coins: {{ wallet.balance }}</span>
|
||||
<div class="flex space-x-4">
|
||||
<form @submit.prevent="addCoins">
|
||||
<input v-model="amount" type="number" class="bg-gray-700 p-2 rounded text-white" placeholder="Enter amount">
|
||||
<button type="submit" class="bg-crypto-green hover:bg-green-600 text-white px-4 py-2 rounded ml-2">Add Coins</button>
|
||||
</form>
|
||||
<form @submit.prevent="redeemPromo">
|
||||
<input v-model="promoCode" type="text" class="bg-gray-700 p-2 rounded text-white" placeholder="Promo code">
|
||||
<button type="submit" class="bg-crypto-green hover:bg-green-600 text-white px-4 py-2 rounded ml-2">Redeem</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 rounded-lg p-4 mb-6">
|
||||
<h3 class="text-lg mb-2">Player Stats</h3>
|
||||
<p>Total Winnings: {{ stats.total_winnings }} Skunk Coins</p>
|
||||
<p>Total Bets: {{ stats.total_bets }} Skunk Coins</p>
|
||||
<p>Play Count: {{ stats.play_count }}</p>
|
||||
<p>Win Streak: {{ stats.win_streak }}</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div v-for="game in games" :key="game.id" class="bg-gray-800 rounded-lg overflow-hidden transform hover:scale-105 transition game-card">
|
||||
<img :src="game.thumbnail_url" :alt="game.name" class="w-full h-48 object-cover">
|
||||
<div class="p-4">
|
||||
<h3 class="text-lg">{{ game.name }}</h3>
|
||||
<button class="bg-crypto-green hover:bg-green-600 text-white px-4 py-2 rounded mt-2">Play Now</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useForm } from '@inertiajs/vue3'; // Use useForm for Inertia POST requests
|
||||
|
||||
defineProps({
|
||||
wallet: Object,
|
||||
games: Array,
|
||||
user: Object,
|
||||
stats: Object,
|
||||
success: String,
|
||||
error: String,
|
||||
});
|
||||
|
||||
const depositForm = useForm({ amount: '' });
|
||||
const promoForm = useForm({ code: '' });
|
||||
|
||||
const addCoins = () => {
|
||||
depositForm.post('/lobby/add-coins', {
|
||||
onSuccess: () => depositForm.reset(),
|
||||
});
|
||||
};
|
||||
|
||||
const redeemPromo = () => {
|
||||
promoForm.post('/lobby/redeem-promo', {
|
||||
onSuccess: () => promoForm.reset(),
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.game-card {
|
||||
@apply transition-all duration-300 hover:shadow-lg hover:shadow-crypto-green/50;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,13 @@
|
||||
import './bootstrap';
|
||||
import { createApp, h } from 'vue';
|
||||
import { createInertiaApp } from '@inertiajs/vue3';
|
||||
|
||||
import Alpine from 'alpinejs';
|
||||
|
||||
window.Alpine = Alpine;
|
||||
|
||||
Alpine.start();
|
||||
createInertiaApp({
|
||||
resolve: name => {
|
||||
const pages = import.meta.glob('./Pages/**/*.vue', { eager: true });
|
||||
return pages[`./Pages/${name}.vue`];
|
||||
},
|
||||
setup({ el, App, props }) {
|
||||
createApp({ render: () => h(App, props) }).mount(el);
|
||||
},
|
||||
});
|
||||
13
resources/views/app.blade.php
Normal file
13
resources/views/app.blade.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Skunk Lounge</title>
|
||||
@vite(['resources/js/app.js', 'resources/css/app.css'])
|
||||
@inertiaHead
|
||||
</head>
|
||||
<body class="bg-dark-bg text-white">
|
||||
@inertia
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,27 +0,0 @@
|
||||
<x-guest-layout>
|
||||
<div class="mb-4 text-sm text-gray-600">
|
||||
{{ __('This is a secure area of the application. Please confirm your password before continuing.') }}
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('password.confirm') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
|
||||
<x-text-input id="password" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password"
|
||||
required autocomplete="current-password" />
|
||||
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<x-primary-button>
|
||||
{{ __('Confirm') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
@@ -1,25 +0,0 @@
|
||||
<x-guest-layout>
|
||||
<div class="mb-4 text-sm text-gray-600">
|
||||
{{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }}
|
||||
</div>
|
||||
|
||||
<!-- Session Status -->
|
||||
<x-auth-session-status class="mb-4" :status="session('status')" />
|
||||
|
||||
<form method="POST" action="{{ route('password.email') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
<x-primary-button>
|
||||
{{ __('Email Password Reset Link') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
@@ -1,47 +0,0 @@
|
||||
<x-guest-layout>
|
||||
<!-- Session Status -->
|
||||
<x-auth-session-status class="mb-4" :status="session('status')" />
|
||||
|
||||
<form method="POST" action="{{ route('login') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
|
||||
<x-text-input id="password" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password"
|
||||
required autocomplete="current-password" />
|
||||
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Remember Me -->
|
||||
<div class="block mt-4">
|
||||
<label for="remember_me" class="inline-flex items-center">
|
||||
<input id="remember_me" type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500" name="remember">
|
||||
<span class="ms-2 text-sm text-gray-600">{{ __('Remember me') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
@if (Route::has('password.request'))
|
||||
<a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('password.request') }}">
|
||||
{{ __('Forgot your password?') }}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<x-primary-button class="ms-3">
|
||||
{{ __('Log in') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
@@ -1,52 +0,0 @@
|
||||
<x-guest-layout>
|
||||
<form method="POST" action="{{ route('register') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Name -->
|
||||
<div>
|
||||
<x-input-label for="name" :value="__('Name')" />
|
||||
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
|
||||
<x-input-error :messages="$errors->get('name')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Email Address -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
|
||||
<x-text-input id="password" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password"
|
||||
required autocomplete="new-password" />
|
||||
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
|
||||
|
||||
<x-text-input id="password_confirmation" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password_confirmation" required autocomplete="new-password" />
|
||||
|
||||
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
<a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('login') }}">
|
||||
{{ __('Already registered?') }}
|
||||
</a>
|
||||
|
||||
<x-primary-button class="ms-4">
|
||||
{{ __('Register') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
@@ -1,39 +0,0 @@
|
||||
<x-guest-layout>
|
||||
<form method="POST" action="{{ route('password.store') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Password Reset Token -->
|
||||
<input type="hidden" name="token" value="{{ $request->route('token') }}">
|
||||
|
||||
<!-- Email Address -->
|
||||
<div>
|
||||
<x-input-label for="email" :value="__('Email')" />
|
||||
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $request->email)" required autofocus autocomplete="username" />
|
||||
<x-input-error :messages="$errors->get('email')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" />
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Confirm Password -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
|
||||
|
||||
<x-text-input id="password_confirmation" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password_confirmation" required autocomplete="new-password" />
|
||||
|
||||
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
<x-primary-button>
|
||||
{{ __('Reset Password') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
@@ -1,31 +0,0 @@
|
||||
<x-guest-layout>
|
||||
<div class="mb-4 text-sm text-gray-600">
|
||||
{{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
|
||||
</div>
|
||||
|
||||
@if (session('status') == 'verification-link-sent')
|
||||
<div class="mb-4 font-medium text-sm text-green-600">
|
||||
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<form method="POST" action="{{ route('verification.send') }}">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<x-primary-button>
|
||||
{{ __('Resend Verification Email') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
<button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
{{ __('Log Out') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</x-guest-layout>
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" {{ $attributes }}>
|
||||
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,7 +0,0 @@
|
||||
@props(['status'])
|
||||
|
||||
@if ($status)
|
||||
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
|
||||
{{ $status }}
|
||||
</div>
|
||||
@endif
|
||||
@@ -1,3 +0,0 @@
|
||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -1 +0,0 @@
|
||||
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
|
||||
@@ -1,35 +0,0 @@
|
||||
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white'])
|
||||
|
||||
@php
|
||||
$alignmentClasses = match ($align) {
|
||||
'left' => 'ltr:origin-top-left rtl:origin-top-right start-0',
|
||||
'top' => 'origin-top',
|
||||
default => 'ltr:origin-top-right rtl:origin-top-left end-0',
|
||||
};
|
||||
|
||||
$width = match ($width) {
|
||||
'48' => 'w-48',
|
||||
default => $width,
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
|
||||
<div @click="open = ! open">
|
||||
{{ $trigger }}
|
||||
</div>
|
||||
|
||||
<div x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
|
||||
style="display: none;"
|
||||
@click="open = false">
|
||||
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
|
||||
{{ $content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,9 +0,0 @@
|
||||
@props(['messages'])
|
||||
|
||||
@if ($messages)
|
||||
<ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}>
|
||||
@foreach ((array) $messages as $message)
|
||||
<li>{{ $message }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
@@ -1,5 +0,0 @@
|
||||
@props(['value'])
|
||||
|
||||
<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700']) }}>
|
||||
{{ $value ?? $slot }}
|
||||
</label>
|
||||
@@ -1,61 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{{ $title ?? 'Skunk Lounge' }}</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Arial&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Bootstrap (for grid/responsiveness) -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
||||
|
||||
<!-- Tailwind CSS -->
|
||||
@vite('resources/css/app.css')
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="{{ asset('js/app.js') }}" defer></script>
|
||||
</head>
|
||||
<body class="bg-dark text-white font-sans antialiased">
|
||||
<!-- Top Bar -->
|
||||
<header class="bg-gray_900 shadow-neon p-4 fixed top-0 left-0 right-0 z-50">
|
||||
<div class="container mx-auto flex justify-between items-center">
|
||||
<a href="/lobby" class="text-2xl font-bold text-neon_green text-glow">Skunk Lounge</a>
|
||||
<div class="flex items-center">
|
||||
<span class="mr-4 text-lg">Balance: ${{ auth()->user()->balance ?? 0 }}</span>
|
||||
<a href="/deposits" class="btn btn-neon">Deposit</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Left Sidebar Menu -->
|
||||
<nav class="fixed top-16 left-0 bottom-0 w-64 bg-gray_900 p-4 shadow-neon z-40 overflow-y-auto">
|
||||
<div class="mb-4">
|
||||
<!-- Logo -->
|
||||
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="w-full mb-4"> <!-- Add logo to public/images if needed -->
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="/lobby" class="block py-2 px-4 hover:bg-gray_800 rounded text-white hover:text-neon_green text-glow">Lobby</a></li>
|
||||
<li><a href="/profile" class="block py-2 px-4 hover:bg-gray_800 rounded text-white hover:text-neon_green text-glow">Profile</a></li>
|
||||
<li><a href="/deposits" class="block py-2 px-4 hover:bg-gray_800 rounded text-white hover:text-neon_green text-glow">Deposits</a></li>
|
||||
<li><a href="/transactions" class="block py-2 px-4 hover:bg-gray_800 rounded text-white hover:text-neon_green text-glow">Transactions</a></li>
|
||||
<li><a href="/vip" class="block py-2 px-4 hover:bg-gray_800 rounded text-white hover:text-neon_green text-glow">VIP</a></li>
|
||||
<li><a href="/about" class="block py-2 px-4 hover:bg-gray_800 rounded text-white hover:text-neon_green text-glow">About Us</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="ml-64 pt-16 p-4 min-h-screen">
|
||||
{{ $slot }}
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-gray_900 p-4 text-center text-gray-500">
|
||||
Fake casino for entertainment only. No real money.
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,78 +0,0 @@
|
||||
@props([
|
||||
'name',
|
||||
'show' => false,
|
||||
'maxWidth' => '2xl'
|
||||
])
|
||||
|
||||
@php
|
||||
$maxWidth = [
|
||||
'sm' => 'sm:max-w-sm',
|
||||
'md' => 'sm:max-w-md',
|
||||
'lg' => 'sm:max-w-lg',
|
||||
'xl' => 'sm:max-w-xl',
|
||||
'2xl' => 'sm:max-w-2xl',
|
||||
][$maxWidth];
|
||||
@endphp
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
show: @js($show),
|
||||
focusables() {
|
||||
// All focusable element types...
|
||||
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
|
||||
return [...$el.querySelectorAll(selector)]
|
||||
// All non-disabled elements...
|
||||
.filter(el => ! el.hasAttribute('disabled'))
|
||||
},
|
||||
firstFocusable() { return this.focusables()[0] },
|
||||
lastFocusable() { return this.focusables().slice(-1)[0] },
|
||||
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
|
||||
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
|
||||
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
|
||||
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
|
||||
}"
|
||||
x-init="$watch('show', value => {
|
||||
if (value) {
|
||||
document.body.classList.add('overflow-y-hidden');
|
||||
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
|
||||
} else {
|
||||
document.body.classList.remove('overflow-y-hidden');
|
||||
}
|
||||
})"
|
||||
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
|
||||
x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null"
|
||||
x-on:close.stop="show = false"
|
||||
x-on:keydown.escape.window="show = false"
|
||||
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
|
||||
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
|
||||
x-show="show"
|
||||
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
|
||||
style="display: {{ $show ? 'block' : 'none' }};"
|
||||
>
|
||||
<div
|
||||
x-show="show"
|
||||
class="fixed inset-0 transform transition-all"
|
||||
x-on:click="show = false"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
>
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
x-show="show"
|
||||
class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,11 +0,0 @@
|
||||
@props(['active'])
|
||||
|
||||
@php
|
||||
$classes = ($active ?? false)
|
||||
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
|
||||
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out';
|
||||
@endphp
|
||||
|
||||
<a {{ $attributes->merge(['class' => $classes]) }}>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
@@ -1,3 +0,0 @@
|
||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -1,11 +0,0 @@
|
||||
@props(['active'])
|
||||
|
||||
@php
|
||||
$classes = ($active ?? false)
|
||||
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
|
||||
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out';
|
||||
@endphp
|
||||
|
||||
<a {{ $attributes->merge(['class' => $classes]) }}>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
@@ -1,3 +0,0 @@
|
||||
<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -1,3 +0,0 @@
|
||||
@props(['disabled' => false])
|
||||
|
||||
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>
|
||||
@@ -1,17 +0,0 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||
{{ __('Dashboard') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900">
|
||||
{{ __("You're logged in!") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -1,3 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
|
||||
</x-filament-panels::page>
|
||||
@@ -1,4 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<h1 class="text-2xl font-bold mb-4">Deposits</h1>
|
||||
<p>Claim promo codes for fake currency. Claim history below (coming soon).</p>
|
||||
</x-filament-panels::page>
|
||||
@@ -1,20 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<h1 class="text-2xl font-bold mb-4">Casino Lobby</h1>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="card bg-gray-800 p-4 rounded">
|
||||
<h3 class="text-lg mb-2">Slots</h3>
|
||||
<p>Pragmatic-style reels and animations.</p>
|
||||
<a href="/games/slots" class="filament-button filament-button-size-sm">Play</a>
|
||||
</div>
|
||||
<div class="card bg-gray-800 p-4 rounded">
|
||||
<h3 class="text-lg mb-2">Plinko</h3>
|
||||
<p>Physics-based drop with multipliers.</p>
|
||||
<a href="/games/plinko" class="filament-button filament-button-size-sm">Play</a>
|
||||
</div>
|
||||
<div class="card bg-gray-800 p-4 rounded">
|
||||
<h3 class="text-lg mb-2">Wheel Spin</h3>
|
||||
<p>Fortune wheel with prizes.</p>
|
||||
<a href="/games/wheel" class="filament-button filament-button-size-sm">Play</a>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament-panels::page>
|
||||
@@ -1,4 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<h1 class="text-2xl font-bold mb-4">Profile</h1>
|
||||
<p>Change password, profile picture, email, add 2FA (coming soon).</p>
|
||||
</x-filament-panels::page>
|
||||
@@ -1,3 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<h1 class="text-2xl font-bold mb-4">Transactions</h1><p>View deposits, withdrawals, wins/losses (coming soon).</p>
|
||||
</x-filament-panels::page>
|
||||
@@ -1,3 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<h1 class="text-2xl font-bold mb-4">About Us</h1><p>Skunk Lounge is a fake casino for entertainment only. No real money.</p>
|
||||
</x-filament-panels::page>
|
||||
@@ -1,4 +0,0 @@
|
||||
<x-filament-widgets::widget>
|
||||
<x-filament::section>
|
||||
<a href="/admin" class="btn-neon">Admin Panel</a>
|
||||
</x-filament-widgets::widget>
|
||||
@@ -1,40 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Skunk Lounge - Fake Casino</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
||||
<style>
|
||||
body { background-color: #111; color: #fff; font-family: Arial, sans-serif; }
|
||||
.glow { text-shadow: 0 0 10px #ff00de; }
|
||||
.shadow-neon { box-shadow: 0 0 15px #ff00de; }
|
||||
.btn-neon { background-color: #ff00de; color: #111; padding: 0.5rem 1rem; border-radius: 0.25rem; box-shadow: 0 0 10px #ff00de; }
|
||||
.btn-neon:hover { box-shadow: 0 0 15px #ff00de; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mx-auto mt-4 bg-gray-900 text-white p-4 rounded shadow-neon">
|
||||
<h1 class="text-3xl font-bold mb-4 glow">Welcome to Skunk Lounge</h1>
|
||||
<p class="mb-4">Self-hosted fake casino for entertainment only. No real money—only fake currency for dopamine rushes. Mimicking Stake.com and Jetmyst.com with sleek navigation and vibrant games.</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||
<div class="card bg-gray-800 p-4 rounded shadow-neon">
|
||||
<h3 class="text-lg mb-2">Games Available</h3>
|
||||
<p class="text-2xl font-bold glow">{{ $gamesCount }}</p>
|
||||
</div>
|
||||
<div class="card bg-gray-800 p-4 rounded shadow-neon">
|
||||
<h3 class="text-lg mb-2">Registered Users</h3>
|
||||
<p class="text-2xl font-bold glow">{{ $usersCount }}</p>
|
||||
</div>
|
||||
<div class="card bg-gray-800 p-4 rounded shadow-neon">
|
||||
<h3 class="text-lg mb-2">Biggest Jackpot Won</h3>
|
||||
<p class="text-2xl font-bold glow">${{ $biggestJackpot }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<a href="{{ route('filament.user.auth.login') }}" class="btn-neon mr-4">Login</a>
|
||||
<a href="{{ route('filament.user.auth.register') }}" class="btn-neon">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user