Files
autocrm/database/migrations/2026_05_28_090000_create_push_subscriptions.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

32 lines
1.0 KiB
PHP

<?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('push_subscriptions', function (Blueprint $t) {
$t->id();
$t->foreignId('company_id')->nullable()->constrained()->cascadeOnDelete();
$t->foreignId('user_id')->nullable()->constrained()->cascadeOnDelete();
$t->string('endpoint', 500);
$t->string('public_key')->nullable(); // p256dh
$t->string('auth_token')->nullable(); // auth
$t->string('content_encoding', 32)->default('aesgcm');
$t->string('user_agent')->nullable();
$t->timestamps();
$t->unique('endpoint', 'push_subscriptions_endpoint_unique');
$t->index(['company_id', 'user_id']);
});
}
public function down(): void
{
Schema::dropIfExists('push_subscriptions');
}
};