diff --git a/Caddyfile b/Caddyfile index 849b4c58..62aeb86b 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,6 +1,8 @@ { frankenphp + order mercure after encode order php_server before file_server + {$GLOBAL_OPTIONS} } # The domain name of your server @@ -12,9 +14,21 @@ dev-loger.neatlancer.com { } mercure { + # Transport to use (default to Bolt) + transport_url {$MERCURE_TRANSPORT_URL:bolt://mercure.db} # Publisher JWT key - publisher_jwt !ChangeThisMercureHubJWTSecretKey! + publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG} # Subscriber JWT key - subscriber_jwt !ChangeThisMercureHubJWTSecretKey! + subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG} + # Permissive configuration for the development environment + cors_origins * + publish_origins * + demo + anonymous + subscriptions + # Extra directives + {$MERCURE_EXTRA_DIRECTIVES} } + + {$CADDY_SERVER_EXTRA_DIRECTIVES} } diff --git a/Caddyfile.dev b/Caddyfile.dev index 196d5351..78e72e8c 100644 --- a/Caddyfile.dev +++ b/Caddyfile.dev @@ -1,6 +1,8 @@ { frankenphp + order mercure after encode order php_server before file_server + {$GLOBAL_OPTIONS} } # The domain name of your server @@ -14,4 +16,23 @@ localhost { # Required for the public/storage/ dir resolve_root_symlink } + + mercure { + # Transport to use (default to Bolt) + transport_url {$MERCURE_TRANSPORT_URL:bolt://mercure.db} + # Publisher JWT key + publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} + # Subscriber JWT key + subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} + # Permissive configuration for the development environment + cors_origins * + publish_origins * + demo + anonymous + subscriptions + # Extra directives + {$MERCURE_EXTRA_DIRECTIVES} + } + + {$CADDY_SERVER_EXTRA_DIRECTIVES} } diff --git a/app/Console/Commands/TestMessaging.php b/app/Console/Commands/TestMessaging.php index 929e633e..3a86ee4b 100644 --- a/app/Console/Commands/TestMessaging.php +++ b/app/Console/Commands/TestMessaging.php @@ -2,9 +2,11 @@ namespace App\Console\Commands; +use Exception; use App\Models\UserDevice; -use App\Services\MessagingService; use Illuminate\Console\Command; +use App\Events\BudgetCalculated; +use App\Services\MessagingService; class TestMessaging extends Command { @@ -27,11 +29,17 @@ class TestMessaging extends Command */ public function handle() { - $service = new MessagingService(); - $userDevice = UserDevice::find(3); - $service->occurrenceType([ - 'title' => 'test', - ], $userDevice->device_id); - echo $userDevice->device_id; + // $service = new MessagingService(); + // $userDevice = UserDevice::find(3); + // $service->occurrenceType([ + // 'title' => 'test', + // ], $userDevice->device_id); + // echo $userDevice->device_id; + try { + broadcast( new BudgetCalculated()); + } catch (Exception $e) { + dd($e); + } + echo "Done"; } } diff --git a/app/Events/BudgetCalculated.php b/app/Events/BudgetCalculated.php index e5ed6408..a9b26a04 100644 --- a/app/Events/BudgetCalculated.php +++ b/app/Events/BudgetCalculated.php @@ -2,35 +2,39 @@ namespace App\Events; -use Illuminate\Broadcasting\Channel; +use App\Models\User; use Illuminate\Queue\SerializesModels; -use Illuminate\Broadcasting\PrivateChannel; -use Illuminate\Broadcasting\PresenceChannel; -use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; +use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; +use Duijker\LaravelMercureBroadcaster\Broadcasting\Channel; -class BudgetCalculated implements ShouldBroadcast +class BudgetCalculated implements ShouldBroadcastNow { - use Dispatchable, InteractsWithSockets, SerializesModels; + use SerializesModels; + public User $user; - /** - * Create a new event instance. - */ - public function __construct() + public function __construct(string $message = "Hello world") { - // + $this->user= User::all()->first(); } - - /** - * Get the channels the event should broadcast on. - * - * @return array - */ public function broadcastOn(): array { return [ - new Channel('http://example/budget-calculated'), + new Channel('https://example.com/main', false), ]; } + + /** + * Get the data to broadcast. + * + * @return array + */ + public function broadcastWith(): array + { + return [ + 'message' => "{$this->user->name} Are you not entertained?" + ]; + } } diff --git a/app/Http/Controllers/Finance/FinanceController.php b/app/Http/Controllers/Finance/FinanceController.php index 0ffef160..8ece196c 100644 --- a/app/Http/Controllers/Finance/FinanceController.php +++ b/app/Http/Controllers/Finance/FinanceController.php @@ -5,6 +5,7 @@ use Carbon\Carbon; use App\Models\Setting; use Illuminate\Http\Request; +use App\Events\BudgetCalculated; use Laravel\Jetstream\Jetstream; use Freesgen\Atmosphere\Http\Querify; use App\Domains\Budget\Models\BudgetMonth; @@ -51,6 +52,8 @@ public function index(Request $request) $savings = BudgetMonthService::getSavingsBalance($teamId, $endDate); $savingsInMonth = BudgetMonthService::getSavingsBalance($teamId, $endDate, $startDate); + BudgetCalculated::dispatch(); + return Jetstream::inertia()->render($request, 'Finance/Index', [ 'sectionTitle' => 'Finance', 'planned' => $this->plannedService->getPlanned($teamId), diff --git a/app/Http/Controllers/System/DashboardController.php b/app/Http/Controllers/System/DashboardController.php index 1e201da9..ad4ee08b 100644 --- a/app/Http/Controllers/System/DashboardController.php +++ b/app/Http/Controllers/System/DashboardController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\System; use Inertia\Inertia; +use App\Events\BudgetCalculated; use App\Domains\Budget\Models\BudgetMonth; use App\Domains\Housing\Models\Occurrence; use App\Domains\Meal\Services\MealService; @@ -33,6 +34,7 @@ public function __invoke() $plannedMeals = $this->mealService->getMealSchedule($teamId); $nextPayments = $this->budgetTargetService->getNextBudgetItems($teamId); + return inertia('Dashboard', [ 'sectionTitle' => 'Dashboard', 'meals' => PlannedMealResource::collection($plannedMeals), diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index 033136ad..c1c550f8 100644 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -12,6 +12,6 @@ class EncryptCookies extends Middleware * @var array */ protected $except = [ - // + 'mercureAuthorization' ]; } diff --git a/app/Http/Middleware/MercureBroarcasterAuthorizationCookie.php b/app/Http/Middleware/MercureBroarcasterAuthorizationCookie.php new file mode 100644 index 00000000..6da8ae65 --- /dev/null +++ b/app/Http/Middleware/MercureBroarcasterAuthorizationCookie.php @@ -0,0 +1,55 @@ +withCookie($this->createCookie($request->user(), $request->secure())); + } + + private function createCookie($user, bool $secure) + { + // Add topic(s) this user has access to + // This can also be URI Templates (to match several topics), or * (to match all topics) + $subscriptions = [ + "http://example/user/{$user->id}/direct-messages", + ]; + + $jwtConfiguration = Configuration::forSymmetricSigner( + new Sha256(), + InMemory::plainText(config('broadcasting.connections.mercure.secret')) + ); + + $token = $jwtConfiguration->builder() + ->withClaim('mercure', ['subscribe' => $subscriptions]) + ->getToken($jwtConfiguration->signer(), $jwtConfiguration->signingKey()) + ->toString(); + + return Cookie::make( + 'mercureAuthorization', + $token, + 15, + '/.well-known/mercure', // or which path you have mercure running + parse_url(config('app.url'), PHP_URL_HOST), + $secure, + true + ); + } +} diff --git a/components.d.ts b/components.d.ts index eabe0679..22e10afb 100644 --- a/components.d.ts +++ b/components.d.ts @@ -7,12 +7,19 @@ export {} declare module 'vue' { export interface GlobalComponents { + IFluentFoodApple20Filled: typeof import('~icons/fluent/food-apple20-filled')['default'] + IMaterialSymbolsBrightnessAlertOutlineRounded: typeof import('~icons/material-symbols/brightness-alert-outline-rounded')['default'] IMdiBankTransfer: typeof import('~icons/mdi/bank-transfer')['default'] IMdiBankTransferIn: typeof import('~icons/mdi/bank-transfer-in')['default'] IMdiBankTransferOut: typeof import('~icons/mdi/bank-transfer-out')['default'] IMdiCallSplit: typeof import('~icons/mdi/call-split')['default'] + IMdiEdit: typeof import('~icons/mdi/edit')['default'] IMdiEllipsisVertical: typeof import('~icons/mdi/ellipsis-vertical')['default'] IMdiFile: typeof import('~icons/mdi/file')['default'] + IMdiLink: typeof import('~icons/mdi/link')['default'] + IMdiLock: typeof import('~icons/mdi/lock')['default'] + IMdiMinus: typeof import('~icons/mdi/minus')['default'] + IMdiPlus: typeof import('~icons/mdi/plus')['default'] IMdiStar: typeof import('~icons/mdi/star')['default'] IMdiStarOutline: typeof import('~icons/mdi/star-outline')['default'] IMdiSync: typeof import('~icons/mdi/sync')['default'] diff --git a/config/broadcasting.php b/config/broadcasting.php index bc6752bb..1cc1873f 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -29,7 +29,6 @@ */ 'connections' => [ - 'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), @@ -63,6 +62,7 @@ 'driver' => 'mercure', 'url' => env('MERCURE_URL', 'http://localhost:3000/.well-known/mercure'), 'secret' => env('MERCURE_SECRET', 'aVerySecretKey'), + 'publisher_key' => env('MERCURE_PUBLISHER_JWT_KEY') ], ], diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index c1b6d385..c84a8f05 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -18,6 +18,16 @@ services: - ./Caddyfile.dev:/etc/caddy/Caddyfile - ./docker-compose/web/timezone:/etc/timezone - loger_db:/var/lib/mysql + environment: + DEBUG: "debug" + # Change the following value if you know how it works. + # MERCURE_TRANSPORT_URL: "bolt://mercure.db" + MERCURE_PUBLISHER_JWT_KEY: "${MERCURE_PUBLISHER_JWT_KEY}" + MERCURE_SUBSCRIBER_JWT_KEY: "${MERCURE_SUBSCRIBER_JWT_KEY}" + # In the cors_origin, add your domain(s). Or just use * + MERCURE_EXTRA_DIRECTIVES: |- + cors_origins "*" + anonymous networks: - loger depends_on: diff --git a/mercure.db b/mercure.db new file mode 100644 index 00000000..46747264 Binary files /dev/null and b/mercure.db differ diff --git a/resources/js/Components/templates/AppGlobals.vue b/resources/js/Components/templates/AppGlobals.vue index 2aebcad0..ddc361c2 100644 --- a/resources/js/Components/templates/AppGlobals.vue +++ b/resources/js/Components/templates/AppGlobals.vue @@ -1,23 +1,4 @@ - - - + + diff --git a/resources/js/config/index.ts b/resources/js/config/index.ts index 5c867748..3b20ad2d 100644 --- a/resources/js/config/index.ts +++ b/resources/js/config/index.ts @@ -10,6 +10,7 @@ interface AppConfig { GOOGLE_APP_CLIENT: string; FIREBASE_VAPID_KEY: string; IS_DEMO: boolean; + MERCURE_URL: string; } const isDemo = import.meta.env?.VITE_APP_DEMO @@ -24,5 +25,6 @@ export const config: AppConfig = { GOOGLE_APP_KEY: import.meta.env.VITE_GOOGLE_APP_KEY, GOOGLE_APP_CLIENT: import.meta.env.VITE_GOOGLE_CLIENT_ID, FIREBASE_VAPID_KEY: import.meta.env.VITE_FIREBASE_VAPID_KEY, - IS_DEMO: Boolean(isDemo) && isDemo !== 'false' + IS_DEMO: Boolean(isDemo) && isDemo !== 'false', + MERCURE_URL: import.meta.env.VITE_MERCURE_URL }