diff --git a/app/Filament/Resources/TicketResource.php b/app/Filament/Resources/TicketResource.php index 7300124..ea5ee56 100644 --- a/app/Filament/Resources/TicketResource.php +++ b/app/Filament/Resources/TicketResource.php @@ -198,6 +198,30 @@ class TicketResource extends Resource ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\BulkAction::make('notify_users') + ->label('Send Email Notification') + ->color('success') + ->icon('heroicon-o-envelope') + ->requiresConfirmation() + ->deselectRecordsAfterCompletion() + ->action(function (\Illuminate\Support\Collection $records) { + foreach ($records as $ticket) { + $responses = $ticket->responses() + ->with('user') + ->orderBy('created_at', 'desc') + ->get(); + + if ($ticket->user && $ticket->user->email) { + \Illuminate\Support\Facades\Mail::to($ticket->user->email) + ->send(new \App\Mail\TicketResponseNotification($ticket, $responses)); + } + } + + \Filament\Notifications\Notification::make() + ->title('Email notifications sent successfully!') + ->success() + ->send(); + }), ]), ]); } diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php new file mode 100644 index 0000000..192b894 --- /dev/null +++ b/app/Filament/Resources/UserResource.php @@ -0,0 +1,159 @@ +schema([ + TextInput::make('name') + ->required() + ->maxLength(255), + + TextInput::make('email') + ->email() + ->required() + ->maxLength(255), + + TextInput::make('stripe_id') + ->label('Stripe ID') + ->disabled() + ->helperText('Automatically managed by Stripe'), + + TextInput::make('pm_type') + ->label('Payment Method Type') + ->disabled(), + + TextInput::make('pm_last_four') + ->label('Card Last 4 Digits') + ->disabled(), + + DatePicker::make('trial_ends_at') + ->label('Trial Ends At') + ->disabled() + ->displayFormat('Y-m-d H:i:s'), + Select::make('level') + ->label('User Level') + ->options([ + 0 => 'Normal User', + 1 => 'Banned', + 9 => 'Super Admin', + ]) + ->required(), + + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('name')->sortable()->searchable(), + TextColumn::make('email')->sortable()->searchable(), + BadgeColumn::make('level') + ->label('User Level') + ->getStateUsing(function ($record) { + return match ($record->level) { + 0 => 'Normal User', + 1 => 'Banned', + 9 => 'Super Admin', + default => 'Unknown', // In case some invalid level exists + }; + }) + ->colors([ + 'success' => fn ($state) => $state === 'Normal User', + 'danger' => fn ($state) => $state === 'Banned', + 'warning' => fn ($state) => $state === 'Super Admin', + ]) + ->sortable(), + TextColumn::make('stripe_id')->label('Stripe ID')->copyable(), + TextColumn::make('pm_last_four')->label('Card Last 4'), + TextColumn::make('trial_ends_at')->label('Trial Ends')->dateTime()->sortable(), + ]) + ->defaultSort('created_at', 'desc') + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\BulkAction::make('updateLevel') + ->label('Update User Level') + ->action(function (Collection $records, array $data) { + + $newLevel = $data['new_level']; + if ($newLevel === 9) { + throw new \Exception('User level cannot be 9 or higher.'); + } + + DB::table('users') + ->whereIn('id', $records->pluck('id')) + ->update(['level' => $newLevel]); + + + Notification::make() + ->title('User Level Updated') + ->body('The selected users\' levels have been updated successfully.') + ->success() + ->send(); + }) + ->icon('heroicon-o-pencil') + ->color('primary') + ->modalHeading('Select User Level') + ->modalSubheading('Please choose the user level to apply to the selected users.') + ->form([ + Select::make('new_level') + ->label('Select User Level') + ->options([ + 0 => 'Unban (Normal User)', + 1 => 'Ban', + ]) + ->required(), + ]), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListUsers::route('/'), + 'create' => Pages\CreateUser::route('/create'), + 'edit' => Pages\EditUser::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/UserResource/Pages/CreateUser.php b/app/Filament/Resources/UserResource/Pages/CreateUser.php new file mode 100644 index 0000000..73aa46d --- /dev/null +++ b/app/Filament/Resources/UserResource/Pages/CreateUser.php @@ -0,0 +1,12 @@ +level === 1) { + // Return the banned page instead of proceeding with the request + return response()->view('banned'); + } + return $next($request); + } +} diff --git a/app/Mail/TicketResponseNotification.php b/app/Mail/TicketResponseNotification.php new file mode 100644 index 0000000..66308d0 --- /dev/null +++ b/app/Mail/TicketResponseNotification.php @@ -0,0 +1,59 @@ +ticket = $ticket; + $this->responses = $responses; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: 'Support Ticket Response: #'. $this->ticket->ticket_id, + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'emails.ticket.response', + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/resources/views/banned.blade.php b/resources/views/banned.blade.php new file mode 100644 index 0000000..7812479 --- /dev/null +++ b/resources/views/banned.blade.php @@ -0,0 +1,43 @@ + + + + + + Account Banned + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + +
+
+ + + + + + +

Account Access Restricted

+

+ We regret to inform you that your account has been temporarily banned due to potential abuse detected by our system. +

+ +
+

+ If you believe this is a mistake, please reach out to us for assistance. +

+ + + Contact Support + + + +

+ Or email us at contact@zemail.me +

+
+
+
+ + + diff --git a/resources/views/emails/ticket/response.blade.php b/resources/views/emails/ticket/response.blade.php new file mode 100644 index 0000000..910a44b --- /dev/null +++ b/resources/views/emails/ticket/response.blade.php @@ -0,0 +1,26 @@ + +# Support Ticket Response : #{{ $ticket->ticket_id }} + +**Subject:** {{ $ticket->subject }} + +**Message:** +{{ $ticket->message }} + + +@foreach ($responses as $response) +**Response from {{ $response->user_id === 1 ? 'Support Team' : $response->user->name }} ({{ $response->created_at->diffForHumans() }}):** + +> {{ $response->response }} + +@if (!$loop->last) +--- +@endif +@endforeach + + +View Ticket + + +Thanks,
+{{ config('app.name') }} +
diff --git a/routes/web.php b/routes/web.php index eab222e..49784bb 100644 --- a/routes/web.php +++ b/routes/web.php @@ -2,6 +2,7 @@ use App\Http\Controllers\AppController; use App\Http\Middleware\CheckPageSlug; +use App\Http\Middleware\CheckUserBanned; use App\Livewire\AddOn; use App\Livewire\Blog; use App\Livewire\Dashboard\Bulk; @@ -54,7 +55,7 @@ Route::get('gmailnator', AddOn::class)->name('gmailnator'); Route::get('emailnator', AddOn::class)->name('emailnator'); Route::get('temp-gmail', AddOn::class)->name('temp-gmail'); -Route::middleware(['auth', 'verified'])->group(function () { +Route::middleware(['auth', 'verified', CheckUserBanned::class])->group(function () { Route::get('dashboard', Dashboard::class)->name('dashboard'); Route::get('dashboard/generate-premium-email', Inbox::class)->name('dashboard.premium'); Route::get('dashboard/generate-10minute-email', Dashboard::class)->name('dashboard.10minute'); @@ -111,7 +112,28 @@ Route::middleware(['auth', 'verified'])->group(function () { return response()->json([ 'message' => trim($output), ]); - }); + })->name('storageLink'); + + Route::get('0xdash/scache', function (Request $request) { + $validUser = 'admin'; + $validPass = 'admin@9608'; // 🔐 Change this to something secure + + if (!isset($_SERVER['PHP_AUTH_USER']) || + $_SERVER['PHP_AUTH_USER'] !== $validUser || + $_SERVER['PHP_AUTH_PW'] !== $validPass) { + + header('WWW-Authenticate: Basic realm="Restricted Area"'); + header('HTTP/1.0 401 Unauthorized'); + echo 'Unauthorized'; + exit; + } + Artisan::call('cache:clear'); + $output = Artisan::output(); + + return response()->json([ + 'message' => trim($output), + ]); + })->name('cacheClear'); });