Files
autocrm/app/Console/Commands/GenerateVapidKeysCommand.php
Vasyka c413004930 Stage 15 — PWA complete: install prompt + Web Push notifications
Dependency:
- minishlink/web-push v10 (VAPID JWT + aes128gcm payload encryption)
- Dockerfile: add curl, mbstring, gmp extensions (web-push needs ext-curl)

VAPID:
- config/webpush.php from env; `php artisan push:vapid` generates keypair
- Shared platform keypair; .env.example has empty placeholders

Schema:
- push_subscriptions (user/company, endpoint unique, p256dh, auth, encoding)

WebPushService:
- send / sendToUser / dispatch via WebPush::flush
- Auto-prunes subscriptions reported expired (404/410)

Subscribe flow:
- POST /push/subscribe + /push/unsubscribe (auth, tenant)
- Tenant panel JS subscribes after SW registration with VAPID public key

Service worker (/sw.js):
- Cache v2, push listener → showNotification, notificationclick → focus/open

Install prompt:
- Floating "Instalează aplicația" button wired to beforeinstallprompt

Staff push:
- WorkOrder master_id change → push to assigned mechanic
- Settings "Test notificare push" action

Tests (6 new):
- subscribe stores + upserts; requires auth (401); validation (422);
  service configured; sendToUser with no subs returns zero

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 05:11:18 +00:00

29 lines
832 B
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Minishlink\WebPush\VAPID;
class GenerateVapidKeysCommand extends Command
{
protected $signature = 'push:vapid';
protected $description = 'Generate a VAPID keypair for Web Push and print the .env lines.';
public function handle(): int
{
$keys = VAPID::createVapidKeys();
$this->info('VAPID keys generated. Add these to your .env:');
$this->newLine();
$this->line('VAPID_SUBJECT=mailto:admin@service.mir.md');
$this->line('VAPID_PUBLIC_KEY=' . $keys['publicKey']);
$this->line('VAPID_PRIVATE_KEY=' . $keys['privateKey']);
$this->newLine();
$this->warn('Keep the private key secret. Re-generating invalidates existing subscriptions.');
return self::SUCCESS;
}
}