Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
bddf8fb
initial reverb fw in place, tested, wip
jeremykenedy Dec 12, 2025
d514416
Apply fixes from StyleCI
StyleCIBot Dec 12, 2025
e438d86
Merge pull request #21 from jeremykenedy/analysis-1kE1kP
jeremykenedy Dec 12, 2025
b5a3582
adding notifications dismissing
jeremykenedy Dec 12, 2025
71e76af
adjust nav profile role badge
jeremykenedy Dec 12, 2025
e1c3137
adjust overflow of widgets, add mark all as dismissed state to notifi…
jeremykenedy Dec 12, 2025
afa2b23
cleanup
jeremykenedy Dec 12, 2025
3c08dc6
Apply fixes from StyleCI
StyleCIBot Dec 12, 2025
8f3264c
Merge pull request #22 from jeremykenedy/analysis-2ZxVJZ
jeremykenedy Dec 12, 2025
319e4af
add notification landing page
jeremykenedy Dec 13, 2025
b9177e7
Apply fixes from StyleCI
StyleCIBot Dec 13, 2025
46455d0
Merge pull request #23 from jeremykenedy/analysis-EEGymD
jeremykenedy Dec 13, 2025
df2298c
resolve merge conflict
jeremykenedy Dec 13, 2025
39ca5a5
Apply fixes from StyleCI
StyleCIBot Dec 13, 2025
0253556
Merge pull request #24 from jeremykenedy/analysis-d1nVGb
jeremykenedy Dec 13, 2025
55fd18f
update ui for notifications dropdown
jeremykenedy Dec 13, 2025
8f0628b
Apply fixes from StyleCI
StyleCIBot Dec 13, 2025
c4654b9
Merge pull request #25 from jeremykenedy/analysis-g07noG
jeremykenedy Dec 13, 2025
813e27a
notifications ui cleanup
jeremykenedy Dec 13, 2025
5df3127
update notifications page, fix social controller to assign default ro…
jeremykenedy Dec 14, 2025
3787ae7
cleanup
jeremykenedy Dec 14, 2025
3ff98c1
all notifications landing page ui updates
jeremykenedy Dec 15, 2025
dfff406
Apply fixes from StyleCI
StyleCIBot Dec 15, 2025
5099af1
Merge pull request #26 from jeremykenedy/analysis-DD756g
jeremykenedy Dec 15, 2025
3d88f25
add safe html for internal component use to pass markup, added soft d…
jeremykenedy Dec 15, 2025
507a90f
Apply fixes from StyleCI
StyleCIBot Dec 15, 2025
619491d
Merge pull request #27 from jeremykenedy/analysis-Lm5pvm
jeremykenedy Dec 15, 2025
d7723df
update all notification page and top nav component to stay in alignment
jeremykenedy Dec 15, 2025
2ff16d1
Apply fixes from StyleCI
StyleCIBot Dec 15, 2025
cc8cee3
Merge pull request #28 from jeremykenedy/analysis-REA7AK
jeremykenedy Dec 15, 2025
e67aa8c
add auto_expire_on to AppNotification
jeremykenedy Dec 15, 2025
54a516f
add release scope as a type of notification
jeremykenedy Dec 16, 2025
2854621
cleanup notifications page
jeremykenedy Dec 16, 2025
3667a83
cleanup notifications page
jeremykenedy Dec 16, 2025
527a3b1
add jobs and artisan commands to cleanup notifications
jeremykenedy Dec 16, 2025
383513b
Apply fixes from StyleCI
StyleCIBot Dec 16, 2025
3419235
Merge pull request #29 from jeremykenedy/analysis-Kg09K6
jeremykenedy Dec 16, 2025
7c822f8
update middleware
jeremykenedy Dec 16, 2025
9e3ffab
add sorting to notifications page
jeremykenedy Dec 16, 2025
3f19a5d
cleanup
jeremykenedy Dec 16, 2025
2b5360b
cleanup
jeremykenedy Dec 16, 2025
ca0e792
add ability to autoschedule notification backend in place
jeremykenedy Dec 16, 2025
6fe8c9c
Apply fixes from StyleCI
StyleCIBot Dec 16, 2025
8e188ea
Merge pull request #30 from jeremykenedy/analysis-JD0R1D
jeremykenedy Dec 16, 2025
4e580a4
move scheduled jobs to app.php
jeremykenedy Dec 16, 2025
1f32f4c
Apply fixes from StyleCI
StyleCIBot Dec 16, 2025
17bf7a8
Merge pull request #31 from jeremykenedy/analysis-jDJbjB
jeremykenedy Dec 16, 2025
eef6237
update admin notifications table
jeremykenedy Dec 17, 2025
857a02d
Apply fixes from StyleCI
StyleCIBot Dec 17, 2025
bf55f0f
Merge pull request #32 from jeremykenedy/analysis-bDav0M
jeremykenedy Dec 17, 2025
e7029bb
update admin notifications table with badges component
jeremykenedy Dec 17, 2025
33381ae
cleanup notification model
jeremykenedy Dec 18, 2025
4d3a57a
update admin notifications table reporting
jeremykenedy Dec 18, 2025
721a971
Apply fixes from StyleCI
StyleCIBot Dec 18, 2025
75cf964
Merge pull request #33 from jeremykenedy/analysis-BAwjk9
jeremykenedy Dec 18, 2025
2c81077
style update
jeremykenedy Dec 18, 2025
c2e6ce3
update docs for notification/websockets/reverb
jeremykenedy Dec 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
SESSION_DOMAIN=guacpanel-official.test
SANCTUM_STATEFUL_DOMAINS=guacpanel-official.test,localhost,127.0.0.1

BROADCAST_CONNECTION=log
BROADCAST_DRIVER=reverb
BROADCAST_CONNECTION=reverb
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database

Expand Down Expand Up @@ -91,6 +93,13 @@ APP_DEMO_ENABLED=false
[email protected]
APP_DEMO_LOGIN_PASSWORD=password

APP_NOTIFICATIONS_ENABLED=true
APP_NOTIFICATIONS_IN_DEMO_MODE=true
APP_NOTIFICATIONS_AUTO_CLEANUP_DELETED_ENABLED=false
APP_NOTIFICATIONS_AUTO_CLEANUP_DELETED_DAYS=60
APP_NOTIFICATIONS_AUTO_CLEANUP_SEND_EMAIL=true
APP_NOTIFICATIONS_AUTO_CLEANUP_SEND_EMAIL_TO="${APP_HELP_EMAIL}"

SEED_SUPER_ADMIN_USER_ENABLED=true
SEED_SUPER_ADMIN_USER_NAME=Ota
[email protected]
Expand Down Expand Up @@ -130,3 +139,20 @@ LINKEDIN_ENABLED=false
LINKEDIN_CLIENT_ID=your-linkedin-client-id
LINKEDIN_SECRET=your-linkedin-client-secret
LINKEDIN_REDIRECT=${APP_URL}/auth/social/linkedin/callback

REVERB_APP_ID=
REVERB_APP_KEY=
REVERB_APP_SECRET=
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=https
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080
REVERB_TLS_CERT=
REVERB_TLS_KEY=

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
VITE_APP_NOTIFICATIONS_ENABLED="${APP_NOTIFICATIONS_ENABLED}"
142 changes: 96 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,65 +24,69 @@ An opinionated Laravel starter kit built with Vue.js, Inertia.js, and Tailwind C
</tr>
</table>


## Features

- 🔐 **Authentication & Security**
- Secure login with [Laravel Fortify](https://laravel.com/docs/fortify)
- Passwordless magic link authentication
- Social Authentication with [Laravel Socialite](https://laravel.com/docs/socialite)
- [Google](https://console.developers.google.com/)
- [GitHub](https://github.com/settings/applications/new) (Will work with local dev callback)
- [Facebook](https://developers.facebook.com/) (Will work with local dev callback)
- [LinkedIn](https://www.linkedin.com/developers/apps/) (Will work with local dev callback)
- Two-factor authentication (2FA) via [Laravel Fortify](https://laravel.com/docs/fortify#two-factor-authentication)
- Role-based permissions with [Spatie Permission](https://spatie.be/docs/laravel-permission)
- Visual role and permission management
- User role assignment interface
- Session and security management
- Active sessions overview
- Login history tracking
- Password policies enforcement
- Secure login with [Laravel Fortify](https://laravel.com/docs/fortify)
- Passwordless magic link authentication
- Social Authentication with [Laravel Socialite](https://laravel.com/docs/socialite)
- [Google](https://console.developers.google.com/)
- [GitHub](https://github.com/settings/applications/new) (Will work with local dev callback)
- [Facebook](https://developers.facebook.com/) (Will work with local dev callback)
- [LinkedIn](https://www.linkedin.com/developers/apps/) (Will work with local dev callback)
- Two-factor authentication (2FA) via [Laravel Fortify](https://laravel.com/docs/fortify#two-factor-authentication)
- Role-based permissions with [Spatie Permission](https://spatie.be/docs/laravel-permission)
- Visual role and permission management
- User role assignment interface
- Session and security management
- Active sessions overview
- Login history tracking
- Password policies enforcement

- 🎨 **Interface & Design**
- Dark/Light mode with system preference detection
- Responsive design with [Tailwind CSS v4](https://tailwindcss.com/docs)
- Auto-generated avatars via [Laravel Avatar](https://github.com/laravolt/avatar)
- Local Google Fonts via [Spatie Laravel Google Fonts](https://github.com/spatie/laravel-google-fonts)
- Customizable theme settings
- Dark/Light mode with system preference detection
- Responsive design with [Tailwind CSS v4](https://tailwindcss.com/docs)
- Auto-generated avatars via [Laravel Avatar](https://github.com/laravolt/avatar)
- Local Google Fonts via [Spatie Laravel Google Fonts](https://github.com/spatie/laravel-google-fonts)
- Customizable theme settings

- 📊 **Data Visualization**
- Interactive charts with [ApexCharts v3](https://apexcharts.com)
- Line, Area, Bar, and Donut charts
- Responsive and mobile-friendly
- Export capabilities
- Automatic data formatting
- Dynamic chart resizing
- Interactive charts with [ApexCharts v3](https://apexcharts.com)
- Line, Area, Bar, and Donut charts
- Responsive and mobile-friendly
- Export capabilities
- Automatic data formatting
- Dynamic chart resizing

- 📊 **Data Tables**
- Modern tables with [@tanstack/vue-table v8](https://tanstack.com/table/v8/docs)
- Server-side pagination
- Column sorting
- Search functionality
- Data export with [Laravel Excel](https://docs.laravel-excel.com)
- Action buttons with confirmation dialogs
- Modern tables with [@tanstack/vue-table v8](https://tanstack.com/table/v8/docs)
- Server-side pagination
- Column sorting
- Search functionality
- Data export with [Laravel Excel](https://docs.laravel-excel.com)
- Action buttons with confirmation dialogs

- 📁 **File Management**
- Drag & drop uploads with [FilePond v4](https://pqina.nl/filepond/docs/)
- Image preview
- File type validation
- Size restrictions
- Multiple file selection
- Drag & drop uploads with [FilePond v4](https://pqina.nl/filepond/docs/)
- Image preview
- File type validation
- Size restrictions
- Multiple file selection

- 🔄 **System Features**
- Backup management via [Spatie Backup](https://spatie.be/docs/laravel-backup)
- User-friendly dashboard
- One-click backup creation
- Backup download
- Activity logging with [Laravel Auditing](https://laravel-auditing.com)
- User action tracking
- Data change history
- Security event monitoring
- Backup management via [Spatie Backup](https://spatie.be/docs/laravel-backup)
- User-friendly dashboard
- One-click backup creation
- Backup download
- Activity logging with [Laravel Auditing](https://laravel-auditing.com)
- User action tracking
- Data change history
- Security event monitoring

- 🔔 **Real-time Notifications (Laravel Reverb)**
- Live, in-app notifications via WebSockets (Reverb)
- Demo mode toggle via `APP_NOTIFICATIONS_IN_DEMO_MODE`
- Run locally with `php artisan reverb:start`

## Quick Start

Expand All @@ -95,24 +99,28 @@ An opinionated Laravel starter kit built with Vue.js, Inertia.js, and Tailwind C
### Installation

1. Clone the repository

```bash
git clone https://github.com/otatechie/guacpanel-tailwind.git
cd guacpanel-tailwind
```

2. Install dependencies

```bash
composer install
npm install
```

3. Set up environment

```bash
cp .env.example .env
php artisan key:generate
```

4. Configure your database in `.env`

```
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
Expand All @@ -123,17 +131,59 @@ DB_PASSWORD=your_password
```

5. Run migrations and seed

```bash
php artisan migrate
php artisan db:seed
```

6. Start development servers

```bash
npm run dev
php artisan serve
```

### Real-time Notifications (Laravel Reverb)

GuacPanel ships with an optional real-time notifications system powered by **Laravel Reverb** (WebSockets).

1. Enable notifications in `.env`

```dotenv
APP_NOTIFICATIONS_ENABLED=true
APP_NOTIFICATIONS_IN_DEMO_MODE=true # set this to false to enable live reverb notifications.
```

2. Install broadcasting + Reverb (generates credentials)

```bash
php artisan install:broadcasting
```

This will populate the following in your `.env` (you can override these as needed):

```dotenv
REVERB_APP_ID=
REVERB_APP_KEY=
REVERB_APP_SECRET=
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=https
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080
REVERB_TLS_CERT=
REVERB_TLS_KEY=
```

3. Start the Reverb server (separate terminal)

```bash
php artisan reverb:start
```

> If you're using Herd / Valet secure domains, you can point `REVERB_TLS_CERT` and `REVERB_TLS_KEY` at the generated certificate + key to use `wss://` in local development.

**🎉 That's it!** Visit `http://localhost:8000` to see the app in action.

**🔗 External Resources**
Expand Down
31 changes: 31 additions & 0 deletions app/Console/Commands/CleanupDeletedAppNotificationsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace App\Console\Commands;

use App\Services\Notifications\AppNotificationCleanupService;
use Illuminate\Console\Command;

class CleanupDeletedAppNotificationsCommand extends Command
{
protected $signature = 'notifications:cleanup-deleted {days? : Days since deleted_at to permanently delete}';

protected $description = 'Permanently deletes soft-deleted app notifications older than N days.';

public function handle(AppNotificationCleanupService $cleanup): int
{
$days = $this->argument('days');

if ($days === null || $days === '') {
$days = (int) config('guacpanel.notifications.auto_cleanup_deleted_days', 30);
}

$days = max(1, (int) $days);

$result = $cleanup->cleanupDeleted($days);

$this->info('Deleted notifications: '.$result['deleted']);
$this->info('Cutoff date: '.$result['cutoff']->toDateTimeString().' ('.$result['days'].' days)');

return self::SUCCESS;
}
}
30 changes: 30 additions & 0 deletions app/Console/Commands/SendScheduledAppNotificationsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Console\Commands;

use App\Services\Notifications\AppNotificationScheduledSendService;
use Illuminate\Console\Command;

class SendScheduledAppNotificationsCommand extends Command
{
protected $signature = 'notifications:send-scheduled {--dry-run : Do not dispatch events or update sent_as_scheduled}';

protected $description = 'Send scheduled notifications that are due (scheduled_on passed, not sent_as_scheduled, not deleted, not expired).';

public function handle(AppNotificationScheduledSendService $service): int
{
$dryRun = (bool) $this->option('dry-run');

$count = $service->sendDue($dryRun);

if ($dryRun) {
$this->info("Dry run: {$count} scheduled notifications are due.");

return self::SUCCESS;
}

$this->info("Sent {$count} scheduled notifications.");

return self::SUCCESS;
}
}
23 changes: 23 additions & 0 deletions app/Console/Commands/SoftDeleteExpiredAppNotificationsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Console\Commands;

use App\Services\Notifications\AppNotificationAutoExpireService;
use Illuminate\Console\Command;

class SoftDeleteExpiredAppNotificationsCommand extends Command
{
protected $signature = 'notifications:soft-delete-expired';

protected $description = 'Soft deletes app notifications whose auto_expire_on is set and has passed.';

public function handle(AppNotificationAutoExpireService $service): int
{
$result = $service->softDeleteExpired();

$this->info('Soft-deleted notifications: '.$result['soft_deleted']);
$this->info('Cutoff (now): '.$result['cutoff']->toDateTimeString());

return self::SUCCESS;
}
}
53 changes: 53 additions & 0 deletions app/Events/AppNotificationCreated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace App\Events;

use App\Models\AppNotification;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class AppNotificationCreated implements ShouldBroadcastNow
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

public function __construct(public AppNotification $notification)
{
}

public function broadcastOn(): Channel
{
if ($this->notification->scope === 'system' || $this->notification->scope === 'release') {
return new PrivateChannel('system');
}

return new PrivateChannel('users.'.$this->notification->user_id);
}

public function broadcastAs(): string
{
return 'app-notification.created';
}

public function broadcastWith(): array
{
$readAt = null;

return [
'id' => $this->notification->id,
'user_id' => $this->notification->user_id,
'scope' => $this->notification->scope,
'type' => $this->notification->type,
'title' => $this->notification->title,
'message' => $this->notification->message,
'data' => $this->notification->data,
'read_at' => $readAt,
'created_at' => optional($this->notification->created_at)?->toISOString(),
];
}
}
Loading
Loading