diff --git a/.gitignore b/.gitignore index df82531..0086d70 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,13 @@ coverage/ /public/build bun.lock bun.lockd + +###> spomky-labs/pwa-bundle ### +/public/idb +/public/pwa +/public/workbox +/public/favicon.ico +/public/site.webmanifest +/public/site.*.webmanifest +/public/sw.js +###< spomky-labs/pwa-bundle ### diff --git a/ansible/templates/caddy.j2 b/ansible/templates/caddy.j2 index 01b94ad..c65ccac 100644 --- a/ansible/templates/caddy.j2 +++ b/ansible/templates/caddy.j2 @@ -19,11 +19,6 @@ www.e-cosplay.fr { } header { -X-Robots-Tag - Permissions-Policy "accelerometer=(), autoplay=(), encrypted-media=(), geolocation=(), gyroscope=(), magnetometer=(), midi=(), payment=(), publickey-credentials-get=(), usb=(), screen-wake-lock=(), xr-spatial-tracking=(), bluetooth=(), gamepad=()" - Content-Security-Policy "base-uri 'self'; default-src 'self'; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com; script-src 'self' 'unsafe-inline' https://browser.sentry-cdn.com https://challenges.cloudflare.com https://sentry.esy-web.dev https://www.e-cosplay.fr https://datas.e-cosplay.fr https://chat.esy-web.dev https://*.cloudflareinsights.com https://storage.googleapis.com https://*.trustpilot.com https://climate.stripe.com; font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com;connect-src https://browser.sentry-cdn.com https://*.e-cosplay.fr https://*.cloudflareinsights.com https://fonts.googleapis.com https://widget.trustpilot.com/ https://challenges.cloudflare.com https://chat.esy-web.dev; frame-src 'self' https://challenges.cloudflare.com https://*.trustpilot.com https://chat.esy-web.dev https://climate.stripe.com; worker-src 'self' blob:;" - Cross-Origin-Embedder-Policy "" - Cross-Origin-Opener-Policy "" - Cross-Origin-Resource-Policy "" X-Frame-Options "SAMEORIGIN" X-Content-Type-options "nosniff" } diff --git a/assets/app.js b/assets/app.js index 22650d7..668d6cc 100644 --- a/assets/app.js +++ b/assets/app.js @@ -1,7 +1,7 @@ import './app.scss' import * as Turbo from "@hotwired/turbo" import '@grafikart/drop-files-element' -import {PaymentForm} from './PaymentForm' +import {PaymentForm} from './PaymentForm.js' import * as Sentry from "@sentry/browser"; // --- CONFIGURATION ET ETAT --- @@ -244,10 +244,6 @@ document.addEventListener('DOMContentLoaded', () => { window.chatwootSDK.run({ websiteToken: '6uFX3g3qybyvSt3PAQUMgkm4', baseUrl: BASE_URL_WOOT }); }; } - const env = document.querySelector('meta[name="env"]'); - if (env && env.getAttribute('content') === "prod" && 'serviceWorker' in navigator) { - navigator.serviceWorker.register('/sw.js'); - } }); document.addEventListener('turbo:load', () => { diff --git a/composer.json b/composer.json index 22ad649..c2538b7 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "mittwald/vault-php": "^3.0.2", "mobiledetect/mobiledetectlib": "^4.8.09", "nelmio/cors-bundle": "^2.6", + "nelmio/security-bundle": "^3.8", "ovh/ovh": ">=3.5", "pear/net_dns2": ">=2.0.7", "phpdocumentor/reflection-docblock": "^5.6.4", @@ -47,6 +48,7 @@ "sentry/sentry-symfony": "^5.6", "setasign/fpdi": "^2.6.4", "spatie/mjml-php": "^1.2.5", + "spomky-labs/pwa-bundle": "^1.4", "stancer/stancer": ">=2.0.1", "stevenmaguire/oauth2-keycloak": "^5.1", "stripe/stripe-php": "^19.0", diff --git a/composer.lock b/composer.lock index 8c62410..2e6c7c8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "403d821dbd4191efa1335b197d907faf", + "content-hash": "4dc12dab62324c75da229e1da20b6ebf", "packages": [ { "name": "adam-paterson/oauth2-stripe", @@ -758,6 +758,78 @@ }, "time": "2024-09-10T14:09:25+00:00" }, + { + "name": "composer/ca-bundle", + "version": "1.5.10", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/961a5e4056dd2e4a2eedcac7576075947c28bf63", + "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.5.10" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-12-08T15:06:51+00:00" + }, { "name": "composer/pcre", "version": "3.3.2", @@ -6169,6 +6241,80 @@ }, "time": "2025-10-23T06:57:22+00:00" }, + { + "name": "nelmio/security-bundle", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/nelmio/NelmioSecurityBundle.git", + "reference": "2fafee1cdda1d5952554c44eef4c3c8566d56f40" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nelmio/NelmioSecurityBundle/zipball/2fafee1cdda1d5952554c44eef4c3c8566d56f40", + "reference": "2fafee1cdda1d5952554c44eef4c3c8566d56f40", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3", + "symfony/framework-bundle": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "symfony/http-kernel": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "symfony/security-core": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "symfony/security-csrf": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "symfony/security-http": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "symfony/yaml": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "ua-parser/uap-php": "^3.4.4" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "phpunit/phpunit": "^9.5 || ^10.1 || ^11.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/browser-kit": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "symfony/cache": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "symfony/phpunit-bridge": "^6.3 || ^7.0 || ^8.0", + "symfony/twig-bundle": "^5.4 || ^6.3 || ^7.0 || ^8.0", + "twig/twig": "^2.10 || ^3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Nelmio\\SecurityBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nelmio", + "homepage": "http://nelm.io" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/nelmio/NelmioSecurityBundle/contributors" + } + ], + "description": "Extra security-related features for Symfony: signed/encrypted cookies, HTTPS/SSL/HSTS handling, cookie session storage, ...", + "keywords": [ + "security" + ], + "support": { + "issues": "https://github.com/nelmio/NelmioSecurityBundle/issues", + "source": "https://github.com/nelmio/NelmioSecurityBundle/tree/v3.8.0" + }, + "time": "2026-01-14T19:38:55+00:00" + }, { "name": "nesbot/carbon", "version": "3.10.3", @@ -8492,6 +8638,115 @@ ], "time": "2025-10-22T08:24:34+00:00" }, + { + "name": "spomky-labs/pwa-bundle", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/Spomky-Labs/pwa-bundle.git", + "reference": "e2dd45a098a3e86d0637e5188c8d616d6c99cdb9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Spomky-Labs/pwa-bundle/zipball/e2dd45a098a3e86d0637e5188c8d616d6c99cdb9", + "reference": "e2dd45a098a3e86d0637e5188c8d616d6c99cdb9", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "phpdocumentor/reflection-docblock": "^5.3|^6.0", + "psr/log": "^1.1|^2.0|^3.0", + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/asset-mapper": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/deprecation-contracts": "^3.5", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^3.0", + "twig/twig": "^3.8" + }, + "require-dev": { + "matthiasnoback/symfony-config-test": "^5.1|^6.0", + "monolog/monolog": "^3.0", + "nelmio/security-bundle": "^3.3", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/monolog-bridge": "^6.4|^7.0|^8.0", + "symfony/monolog-bundle": "^3.10|^4.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/twig-bundle": "^7.0|^8.0", + "symfony/ux-icons": "^2.29", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "suggest": { + "ext-gd": "Required to generate icons (or Imagick).", + "ext-imagick": "Required to generate icons (or GD).", + "symfony/filesystem": "For generating and manipulating icons or screenshots", + "symfony/mime": "For generating and manipulating icons or screenshots", + "symfony/ux-icons": "For using SVG icons (e.g. for shortcuts)" + }, + "type": "symfony-bundle", + "extra": { + "thanks": { + "url": "https://github.com/spomky-labs/pwa-bundle", + "name": "spomky-labs/pwa-bundle" + } + }, + "autoload": { + "psr-4": { + "SpomkyLabs\\PwaBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Florent Morselli", + "homepage": "https://github.com/Spomky" + }, + { + "name": "All contributors", + "homepage": "https://github.com/spomky-labs/pwa-bundle/contributors" + } + ], + "description": "Progressive Web App Manifest Generator Bundle for Symfony.", + "homepage": "https://github.com/spomky-labs", + "keywords": [ + "bundle", + "pwa", + "symfony", + "symfony-ux" + ], + "support": { + "issues": "https://github.com/Spomky-Labs/pwa-bundle/issues", + "source": "https://github.com/Spomky-Labs/pwa-bundle/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.buymeacoffee.com/FlorentMorselli", + "type": "custom" + }, + { + "url": "https://github.com/Spomky", + "type": "github" + }, + { + "url": "https://www.patreon.com/FlorentMorselli", + "type": "patreon" + } + ], + "time": "2026-01-09T10:52:16+00:00" + }, { "name": "stancer/stancer", "version": "v2.0.1", @@ -14914,6 +15169,69 @@ ], "time": "2025-10-29T15:56:47+00:00" }, + { + "name": "ua-parser/uap-php", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/ua-parser/uap-php.git", + "reference": "f44bdd1b38198801cf60b0681d2d842980e47af5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ua-parser/uap-php/zipball/f44bdd1b38198801cf60b0681d2d842980e47af5", + "reference": "f44bdd1b38198801cf60b0681d2d842980e47af5", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.1", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.33", + "phpunit/phpunit": "^8 || ^9", + "symfony/console": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "symfony/filesystem": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "symfony/finder": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "symfony/yaml": "^3.4 || ^4.2 || ^4.3 || ^5.0", + "vimeo/psalm": "^3.12" + }, + "suggest": { + "symfony/console": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0", + "symfony/filesystem": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0", + "symfony/finder": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0", + "symfony/yaml": "Required for CLI usage - ^3.4 || ^4.3 || ^5.0" + }, + "bin": [ + "bin/uaparser" + ], + "type": "library", + "autoload": { + "psr-4": { + "UAParser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dave Olsen", + "email": "dmolsen@gmail.com" + }, + { + "name": "Lars Strojny", + "email": "lars@strojny.net" + } + ], + "description": "A multi-language port of Browserscope's user agent parser.", + "support": { + "issues": "https://github.com/ua-parser/uap-php/issues", + "source": "https://github.com/ua-parser/uap-php/tree/v3.10.0" + }, + "time": "2025-07-17T15:43:24+00:00" + }, { "name": "vich/uploader-bundle", "version": "v2.8.1", diff --git a/config/bundles.php b/config/bundles.php index 1a18dc7..17f43df 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -20,4 +20,6 @@ return [ Sentry\SentryBundle\SentryBundle::class => ['prod' => true], PixelOpen\CloudflareTurnstileBundle\PixelOpenCloudflareTurnstileBundle::class => ['all' => true], KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true], + SpomkyLabs\PwaBundle\SpomkyLabsPwaBundle::class => ['all' => true], + Nelmio\SecurityBundle\NelmioSecurityBundle::class => ['all' => true], ]; diff --git a/config/packages/dev/nelmio_security.yaml b/config/packages/dev/nelmio_security.yaml new file mode 100644 index 0000000..f028c4d --- /dev/null +++ b/config/packages/dev/nelmio_security.yaml @@ -0,0 +1,8 @@ +nelmio_security: + csp: + enforce: + script-src: + - "http://localhost:5173" + connect-src: + - "ws://localhost:5173/" # Autorise le WebSocket de Vite + diff --git a/config/packages/dev/pwa.yaml b/config/packages/dev/pwa.yaml new file mode 100644 index 0000000..9bc74ae --- /dev/null +++ b/config/packages/dev/pwa.yaml @@ -0,0 +1,3 @@ +pwa: + serviceworker: + enabled: false diff --git a/config/packages/nelmio_security.yaml b/config/packages/nelmio_security.yaml new file mode 100644 index 0000000..e32e5a9 --- /dev/null +++ b/config/packages/nelmio_security.yaml @@ -0,0 +1,61 @@ +nelmio_security: + # Content Security Policy (CSP) + referrer_policy: + enabled: true + policies: + - 'strict-origin-when-cross-origin' + permissions_policy: + enabled: true + policies: + camera: [self] # Correct : sans les guillemets simples internes + microphone: [self] # Correct + geolocation: [self] # Correct + fullscreen: [self] # Correct + payment: [self] # Correct + # Si tu veux bloquer une fonction pour tout le monde : + usb: [] + csp: + hash: + algorithm: 'sha256' + enforce: + default-src: ["'self'"] + worker-src: ["'self'"] + script-src: + - "'self'" + - "nonce" + - "https://sentry.esy-web.dev" + - "https://chat.esy-web.dev" + - "https://static.cloudflareinsights.com" + - "https://challenges.cloudflare.com" + connect-src: + - "'self'" + - "https://sentry.esy-web.dev" + - "https://chat.esy-web.dev" + - "https://auth.esy-web.dev" + - "https://cloudflareinsights.com" + - "https://challenges.cloudflare.com" + - "https://tools-security.esy-web.dev" + - "https://checkout.stripe.com/" + frame-src: + - "'self'" + - "https://chat.esy-web.dev" + - "https://challenges.cloudflare.com" + - "https://climate.stripe.com/" + style-src: + - "'self'" + - "'unsafe-inline'" + - "https://fonts.googleapis.com" + - "https://chat.esy-web.dev" + - "https://cdnjs.cloudflare.com" + img-src: + - "'self'" + - "data:" + - "https://chat.esy-web.dev" + font-src: + - "'self'" + - "data:" + - 'https://fonts.gstatic.com' + - "https://cdnjs.cloudflare.com/" + frame-ancestors: ["'none'"] + # Optionnel : forcer le passage en HTTPS + upgrade-insecure-requests: false diff --git a/config/packages/pwa.yaml b/config/packages/pwa.yaml new file mode 100644 index 0000000..cbcb687 --- /dev/null +++ b/config/packages/pwa.yaml @@ -0,0 +1,23 @@ +pwa: + asset_compiler: false # Default to true. Will change to false in 2.0.0. + image_processor: 'pwa.image_processor.gd' # Or 'pwa.image_processor.gd' + favicons: + enabled: true + src: '%kernel.project_dir%/public/assets/notif.png' + serviceworker: + enabled: true + scope: "/" + use_cache: false + skip_waiting: true + manifest: + enabled: true + name: "E-Cosplay" + short_name: "PWA" + start_url: "app_home" + display: "standalone" + background_color: "#ffffff" + theme_color: "#4285f4" + categories: ['games','multimedia','social networking'] + icons: + - src: '%kernel.project_dir%/public/assets/notif.png' + sizes: [192] diff --git a/config/services.yaml b/config/services.yaml index daf94da..5f4b353 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -20,6 +20,8 @@ services: arguments: $manifest: '%kernel.project_dir%/public/build/.vite/manifest.json' $cache: '@vite_cache_pool' + $cspListener: '@nelmio_security.csp_listener' + # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones App\VichUploader\Namer\Account\AvatarName: diff --git a/package.json b/package.json index d9ddda3..7e59424 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ }, "devDependencies": { "@hotwired/stimulus": "^3.2.2", + "@spomky-labs/pwa-bundle": "file:vendor/spomky-labs/pwa-bundle/assets", "@tailwindcss/postcss": "^4.1.17", + "idb": "^8.0", + "idb-keyval": "^6.2", "postcss": "^8.5.6", "postcss-scss": "^4.0.9", "rollup-plugin-javascript-obfuscator": "^1.0.4", diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index c472c2c..0000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/favicon/android/android-launchericon-144-144.png b/public/favicon/android/android-launchericon-144-144.png deleted file mode 100644 index 4cdcaa6..0000000 Binary files a/public/favicon/android/android-launchericon-144-144.png and /dev/null differ diff --git a/public/favicon/android/android-launchericon-192-192.png b/public/favicon/android/android-launchericon-192-192.png deleted file mode 100644 index 14a2f53..0000000 Binary files a/public/favicon/android/android-launchericon-192-192.png and /dev/null differ diff --git a/public/favicon/android/android-launchericon-48-48.png b/public/favicon/android/android-launchericon-48-48.png deleted file mode 100644 index c8fcf22..0000000 Binary files a/public/favicon/android/android-launchericon-48-48.png and /dev/null differ diff --git a/public/favicon/android/android-launchericon-512-512.png b/public/favicon/android/android-launchericon-512-512.png deleted file mode 100644 index e49c3f9..0000000 Binary files a/public/favicon/android/android-launchericon-512-512.png and /dev/null differ diff --git a/public/favicon/android/android-launchericon-72-72.png b/public/favicon/android/android-launchericon-72-72.png deleted file mode 100644 index 13a14ca..0000000 Binary files a/public/favicon/android/android-launchericon-72-72.png and /dev/null differ diff --git a/public/favicon/android/android-launchericon-96-96.png b/public/favicon/android/android-launchericon-96-96.png deleted file mode 100644 index ba6b474..0000000 Binary files a/public/favicon/android/android-launchericon-96-96.png and /dev/null differ diff --git a/public/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png deleted file mode 100644 index e8c7fd6..0000000 Binary files a/public/favicon/apple-touch-icon.png and /dev/null differ diff --git a/public/favicon/favicon-96x96.png b/public/favicon/favicon-96x96.png deleted file mode 100644 index 159b2cb..0000000 Binary files a/public/favicon/favicon-96x96.png and /dev/null differ diff --git a/public/favicon/favicon.ico b/public/favicon/favicon.ico deleted file mode 100644 index c472c2c..0000000 Binary files a/public/favicon/favicon.ico and /dev/null differ diff --git a/public/favicon/favicon.svg b/public/favicon/favicon.svg deleted file mode 100644 index 984ca13..0000000 --- a/public/favicon/favicon.svg +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/public/favicon/ios/100.png b/public/favicon/ios/100.png deleted file mode 100644 index 9515ac8..0000000 Binary files a/public/favicon/ios/100.png and /dev/null differ diff --git a/public/favicon/ios/1024.png b/public/favicon/ios/1024.png deleted file mode 100644 index 36e08ce..0000000 Binary files a/public/favicon/ios/1024.png and /dev/null differ diff --git a/public/favicon/ios/114.png b/public/favicon/ios/114.png deleted file mode 100644 index 71c38a2..0000000 Binary files a/public/favicon/ios/114.png and /dev/null differ diff --git a/public/favicon/ios/120.png b/public/favicon/ios/120.png deleted file mode 100644 index 3e49dc5..0000000 Binary files a/public/favicon/ios/120.png and /dev/null differ diff --git a/public/favicon/ios/128.png b/public/favicon/ios/128.png deleted file mode 100644 index a340856..0000000 Binary files a/public/favicon/ios/128.png and /dev/null differ diff --git a/public/favicon/ios/144.png b/public/favicon/ios/144.png deleted file mode 100644 index 4cdcaa6..0000000 Binary files a/public/favicon/ios/144.png and /dev/null differ diff --git a/public/favicon/ios/152.png b/public/favicon/ios/152.png deleted file mode 100644 index 216afb3..0000000 Binary files a/public/favicon/ios/152.png and /dev/null differ diff --git a/public/favicon/ios/16.png b/public/favicon/ios/16.png deleted file mode 100644 index 021e1f9..0000000 Binary files a/public/favicon/ios/16.png and /dev/null differ diff --git a/public/favicon/ios/167.png b/public/favicon/ios/167.png deleted file mode 100644 index 9484ea6..0000000 Binary files a/public/favicon/ios/167.png and /dev/null differ diff --git a/public/favicon/ios/180.png b/public/favicon/ios/180.png deleted file mode 100644 index 1a39b23..0000000 Binary files a/public/favicon/ios/180.png and /dev/null differ diff --git a/public/favicon/ios/192.png b/public/favicon/ios/192.png deleted file mode 100644 index 14a2f53..0000000 Binary files a/public/favicon/ios/192.png and /dev/null differ diff --git a/public/favicon/ios/20.png b/public/favicon/ios/20.png deleted file mode 100644 index a1d8cbf..0000000 Binary files a/public/favicon/ios/20.png and /dev/null differ diff --git a/public/favicon/ios/256.png b/public/favicon/ios/256.png deleted file mode 100644 index 1f81b96..0000000 Binary files a/public/favicon/ios/256.png and /dev/null differ diff --git a/public/favicon/ios/29.png b/public/favicon/ios/29.png deleted file mode 100644 index 62c0a3a..0000000 Binary files a/public/favicon/ios/29.png and /dev/null differ diff --git a/public/favicon/ios/32.png b/public/favicon/ios/32.png deleted file mode 100644 index d5dafbd..0000000 Binary files a/public/favicon/ios/32.png and /dev/null differ diff --git a/public/favicon/ios/40.png b/public/favicon/ios/40.png deleted file mode 100644 index 656e516..0000000 Binary files a/public/favicon/ios/40.png and /dev/null differ diff --git a/public/favicon/ios/50.png b/public/favicon/ios/50.png deleted file mode 100644 index a467c54..0000000 Binary files a/public/favicon/ios/50.png and /dev/null differ diff --git a/public/favicon/ios/512.png b/public/favicon/ios/512.png deleted file mode 100644 index e49c3f9..0000000 Binary files a/public/favicon/ios/512.png and /dev/null differ diff --git a/public/favicon/ios/57.png b/public/favicon/ios/57.png deleted file mode 100644 index 02c2f67..0000000 Binary files a/public/favicon/ios/57.png and /dev/null differ diff --git a/public/favicon/ios/58.png b/public/favicon/ios/58.png deleted file mode 100644 index 947648a..0000000 Binary files a/public/favicon/ios/58.png and /dev/null differ diff --git a/public/favicon/ios/60.png b/public/favicon/ios/60.png deleted file mode 100644 index 16fe98a..0000000 Binary files a/public/favicon/ios/60.png and /dev/null differ diff --git a/public/favicon/ios/64.png b/public/favicon/ios/64.png deleted file mode 100644 index ec8531d..0000000 Binary files a/public/favicon/ios/64.png and /dev/null differ diff --git a/public/favicon/ios/72.png b/public/favicon/ios/72.png deleted file mode 100644 index 13a14ca..0000000 Binary files a/public/favicon/ios/72.png and /dev/null differ diff --git a/public/favicon/ios/76.png b/public/favicon/ios/76.png deleted file mode 100644 index 49df3df..0000000 Binary files a/public/favicon/ios/76.png and /dev/null differ diff --git a/public/favicon/ios/80.png b/public/favicon/ios/80.png deleted file mode 100644 index 4941fa1..0000000 Binary files a/public/favicon/ios/80.png and /dev/null differ diff --git a/public/favicon/ios/87.png b/public/favicon/ios/87.png deleted file mode 100644 index f3da136..0000000 Binary files a/public/favicon/ios/87.png and /dev/null differ diff --git a/public/favicon/web-app-manifest-192x192.png b/public/favicon/web-app-manifest-192x192.png deleted file mode 100644 index 50844d3..0000000 Binary files a/public/favicon/web-app-manifest-192x192.png and /dev/null differ diff --git a/public/favicon/web-app-manifest-512x512.png b/public/favicon/web-app-manifest-512x512.png deleted file mode 100644 index 1878e9c..0000000 Binary files a/public/favicon/web-app-manifest-512x512.png and /dev/null differ diff --git a/public/favicon/windows11/LargeTile.scale-100.png b/public/favicon/windows11/LargeTile.scale-100.png deleted file mode 100644 index c8a49ff..0000000 Binary files a/public/favicon/windows11/LargeTile.scale-100.png and /dev/null differ diff --git a/public/favicon/windows11/LargeTile.scale-125.png b/public/favicon/windows11/LargeTile.scale-125.png deleted file mode 100644 index 911882f..0000000 Binary files a/public/favicon/windows11/LargeTile.scale-125.png and /dev/null differ diff --git a/public/favicon/windows11/LargeTile.scale-150.png b/public/favicon/windows11/LargeTile.scale-150.png deleted file mode 100644 index 6d2c66b..0000000 Binary files a/public/favicon/windows11/LargeTile.scale-150.png and /dev/null differ diff --git a/public/favicon/windows11/LargeTile.scale-200.png b/public/favicon/windows11/LargeTile.scale-200.png deleted file mode 100644 index 45303da..0000000 Binary files a/public/favicon/windows11/LargeTile.scale-200.png and /dev/null differ diff --git a/public/favicon/windows11/LargeTile.scale-400.png b/public/favicon/windows11/LargeTile.scale-400.png deleted file mode 100644 index b674bf6..0000000 Binary files a/public/favicon/windows11/LargeTile.scale-400.png and /dev/null differ diff --git a/public/favicon/windows11/SmallTile.scale-100.png b/public/favicon/windows11/SmallTile.scale-100.png deleted file mode 100644 index 1bc14ee..0000000 Binary files a/public/favicon/windows11/SmallTile.scale-100.png and /dev/null differ diff --git a/public/favicon/windows11/SmallTile.scale-125.png b/public/favicon/windows11/SmallTile.scale-125.png deleted file mode 100644 index 3aa2bdc..0000000 Binary files a/public/favicon/windows11/SmallTile.scale-125.png and /dev/null differ diff --git a/public/favicon/windows11/SmallTile.scale-150.png b/public/favicon/windows11/SmallTile.scale-150.png deleted file mode 100644 index 4449f9e..0000000 Binary files a/public/favicon/windows11/SmallTile.scale-150.png and /dev/null differ diff --git a/public/favicon/windows11/SmallTile.scale-200.png b/public/favicon/windows11/SmallTile.scale-200.png deleted file mode 100644 index de670b8..0000000 Binary files a/public/favicon/windows11/SmallTile.scale-200.png and /dev/null differ diff --git a/public/favicon/windows11/SmallTile.scale-400.png b/public/favicon/windows11/SmallTile.scale-400.png deleted file mode 100644 index dd60a38..0000000 Binary files a/public/favicon/windows11/SmallTile.scale-400.png and /dev/null differ diff --git a/public/favicon/windows11/SplashScreen.scale-100.png b/public/favicon/windows11/SplashScreen.scale-100.png deleted file mode 100644 index f4dc567..0000000 Binary files a/public/favicon/windows11/SplashScreen.scale-100.png and /dev/null differ diff --git a/public/favicon/windows11/SplashScreen.scale-125.png b/public/favicon/windows11/SplashScreen.scale-125.png deleted file mode 100644 index 42d11cf..0000000 Binary files a/public/favicon/windows11/SplashScreen.scale-125.png and /dev/null differ diff --git a/public/favicon/windows11/SplashScreen.scale-150.png b/public/favicon/windows11/SplashScreen.scale-150.png deleted file mode 100644 index 6c3b2b2..0000000 Binary files a/public/favicon/windows11/SplashScreen.scale-150.png and /dev/null differ diff --git a/public/favicon/windows11/SplashScreen.scale-200.png b/public/favicon/windows11/SplashScreen.scale-200.png deleted file mode 100644 index bb2b624..0000000 Binary files a/public/favicon/windows11/SplashScreen.scale-200.png and /dev/null differ diff --git a/public/favicon/windows11/SplashScreen.scale-400.png b/public/favicon/windows11/SplashScreen.scale-400.png deleted file mode 100644 index 27788f4..0000000 Binary files a/public/favicon/windows11/SplashScreen.scale-400.png and /dev/null differ diff --git a/public/favicon/windows11/Square150x150Logo.scale-100.png b/public/favicon/windows11/Square150x150Logo.scale-100.png deleted file mode 100644 index bb7b40f..0000000 Binary files a/public/favicon/windows11/Square150x150Logo.scale-100.png and /dev/null differ diff --git a/public/favicon/windows11/Square150x150Logo.scale-125.png b/public/favicon/windows11/Square150x150Logo.scale-125.png deleted file mode 100644 index e2769a7..0000000 Binary files a/public/favicon/windows11/Square150x150Logo.scale-125.png and /dev/null differ diff --git a/public/favicon/windows11/Square150x150Logo.scale-150.png b/public/favicon/windows11/Square150x150Logo.scale-150.png deleted file mode 100644 index f1dd447..0000000 Binary files a/public/favicon/windows11/Square150x150Logo.scale-150.png and /dev/null differ diff --git a/public/favicon/windows11/Square150x150Logo.scale-200.png b/public/favicon/windows11/Square150x150Logo.scale-200.png deleted file mode 100644 index c378f4c..0000000 Binary files a/public/favicon/windows11/Square150x150Logo.scale-200.png and /dev/null differ diff --git a/public/favicon/windows11/Square150x150Logo.scale-400.png b/public/favicon/windows11/Square150x150Logo.scale-400.png deleted file mode 100644 index a30aca0..0000000 Binary files a/public/favicon/windows11/Square150x150Logo.scale-400.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png deleted file mode 100644 index 2863f1f..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png deleted file mode 100644 index 5903e12..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png deleted file mode 100644 index e43a8b4..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png deleted file mode 100644 index de32f70..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png deleted file mode 100644 index ab84305..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png deleted file mode 100644 index 31b412a..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png deleted file mode 100644 index 9d3c3fd..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png deleted file mode 100644 index 1c87231..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png deleted file mode 100644 index 4c5083a..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png deleted file mode 100644 index e587703..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png deleted file mode 100644 index 1f8d22c..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png deleted file mode 100644 index 40449a7..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png deleted file mode 100644 index 118513d..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png deleted file mode 100644 index 3a463ff..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png b/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png deleted file mode 100644 index ee07226..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-16.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-16.png deleted file mode 100644 index 2863f1f..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-16.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-20.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-20.png deleted file mode 100644 index 5903e12..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-20.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-24.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-24.png deleted file mode 100644 index e43a8b4..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-24.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-256.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-256.png deleted file mode 100644 index de32f70..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-256.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-30.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-30.png deleted file mode 100644 index ab84305..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-30.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-32.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-32.png deleted file mode 100644 index 31b412a..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-32.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-36.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-36.png deleted file mode 100644 index 9d3c3fd..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-36.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-40.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-40.png deleted file mode 100644 index 1c87231..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-40.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-44.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-44.png deleted file mode 100644 index 4c5083a..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-44.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-48.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-48.png deleted file mode 100644 index e587703..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-48.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-60.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-60.png deleted file mode 100644 index 1f8d22c..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-60.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-64.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-64.png deleted file mode 100644 index 40449a7..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-64.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-72.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-72.png deleted file mode 100644 index 118513d..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-72.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-80.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-80.png deleted file mode 100644 index 3a463ff..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-80.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-96.png b/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-96.png deleted file mode 100644 index ee07226..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-96.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.scale-100.png b/public/favicon/windows11/Square44x44Logo.scale-100.png deleted file mode 100644 index 4c5083a..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.scale-100.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.scale-125.png b/public/favicon/windows11/Square44x44Logo.scale-125.png deleted file mode 100644 index bf25457..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.scale-125.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.scale-150.png b/public/favicon/windows11/Square44x44Logo.scale-150.png deleted file mode 100644 index a2b20b9..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.scale-150.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.scale-200.png b/public/favicon/windows11/Square44x44Logo.scale-200.png deleted file mode 100644 index e159886..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.scale-200.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.scale-400.png b/public/favicon/windows11/Square44x44Logo.scale-400.png deleted file mode 100644 index ea79538..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.scale-400.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-16.png b/public/favicon/windows11/Square44x44Logo.targetsize-16.png deleted file mode 100644 index 2863f1f..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-16.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-20.png b/public/favicon/windows11/Square44x44Logo.targetsize-20.png deleted file mode 100644 index 5903e12..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-20.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-24.png b/public/favicon/windows11/Square44x44Logo.targetsize-24.png deleted file mode 100644 index e43a8b4..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-24.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-256.png b/public/favicon/windows11/Square44x44Logo.targetsize-256.png deleted file mode 100644 index de32f70..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-256.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-30.png b/public/favicon/windows11/Square44x44Logo.targetsize-30.png deleted file mode 100644 index ab84305..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-30.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-32.png b/public/favicon/windows11/Square44x44Logo.targetsize-32.png deleted file mode 100644 index 31b412a..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-32.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-36.png b/public/favicon/windows11/Square44x44Logo.targetsize-36.png deleted file mode 100644 index 9d3c3fd..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-36.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-40.png b/public/favicon/windows11/Square44x44Logo.targetsize-40.png deleted file mode 100644 index 1c87231..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-40.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-44.png b/public/favicon/windows11/Square44x44Logo.targetsize-44.png deleted file mode 100644 index 4c5083a..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-44.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-48.png b/public/favicon/windows11/Square44x44Logo.targetsize-48.png deleted file mode 100644 index e587703..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-48.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-60.png b/public/favicon/windows11/Square44x44Logo.targetsize-60.png deleted file mode 100644 index 1f8d22c..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-60.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-64.png b/public/favicon/windows11/Square44x44Logo.targetsize-64.png deleted file mode 100644 index 40449a7..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-64.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-72.png b/public/favicon/windows11/Square44x44Logo.targetsize-72.png deleted file mode 100644 index 118513d..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-72.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-80.png b/public/favicon/windows11/Square44x44Logo.targetsize-80.png deleted file mode 100644 index 3a463ff..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-80.png and /dev/null differ diff --git a/public/favicon/windows11/Square44x44Logo.targetsize-96.png b/public/favicon/windows11/Square44x44Logo.targetsize-96.png deleted file mode 100644 index ee07226..0000000 Binary files a/public/favicon/windows11/Square44x44Logo.targetsize-96.png and /dev/null differ diff --git a/public/favicon/windows11/StoreLogo.scale-100.png b/public/favicon/windows11/StoreLogo.scale-100.png deleted file mode 100644 index a467c54..0000000 Binary files a/public/favicon/windows11/StoreLogo.scale-100.png and /dev/null differ diff --git a/public/favicon/windows11/StoreLogo.scale-125.png b/public/favicon/windows11/StoreLogo.scale-125.png deleted file mode 100644 index dbda335..0000000 Binary files a/public/favicon/windows11/StoreLogo.scale-125.png and /dev/null differ diff --git a/public/favicon/windows11/StoreLogo.scale-150.png b/public/favicon/windows11/StoreLogo.scale-150.png deleted file mode 100644 index a9fefd4..0000000 Binary files a/public/favicon/windows11/StoreLogo.scale-150.png and /dev/null differ diff --git a/public/favicon/windows11/StoreLogo.scale-200.png b/public/favicon/windows11/StoreLogo.scale-200.png deleted file mode 100644 index 9515ac8..0000000 Binary files a/public/favicon/windows11/StoreLogo.scale-200.png and /dev/null differ diff --git a/public/favicon/windows11/StoreLogo.scale-400.png b/public/favicon/windows11/StoreLogo.scale-400.png deleted file mode 100644 index 182c2af..0000000 Binary files a/public/favicon/windows11/StoreLogo.scale-400.png and /dev/null differ diff --git a/public/favicon/windows11/Wide310x150Logo.scale-100.png b/public/favicon/windows11/Wide310x150Logo.scale-100.png deleted file mode 100644 index e2c7a42..0000000 Binary files a/public/favicon/windows11/Wide310x150Logo.scale-100.png and /dev/null differ diff --git a/public/favicon/windows11/Wide310x150Logo.scale-125.png b/public/favicon/windows11/Wide310x150Logo.scale-125.png deleted file mode 100644 index 90f14f4..0000000 Binary files a/public/favicon/windows11/Wide310x150Logo.scale-125.png and /dev/null differ diff --git a/public/favicon/windows11/Wide310x150Logo.scale-150.png b/public/favicon/windows11/Wide310x150Logo.scale-150.png deleted file mode 100644 index c05e6f4..0000000 Binary files a/public/favicon/windows11/Wide310x150Logo.scale-150.png and /dev/null differ diff --git a/public/favicon/windows11/Wide310x150Logo.scale-200.png b/public/favicon/windows11/Wide310x150Logo.scale-200.png deleted file mode 100644 index f4dc567..0000000 Binary files a/public/favicon/windows11/Wide310x150Logo.scale-200.png and /dev/null differ diff --git a/public/favicon/windows11/Wide310x150Logo.scale-400.png b/public/favicon/windows11/Wide310x150Logo.scale-400.png deleted file mode 100644 index bb2b624..0000000 Binary files a/public/favicon/windows11/Wide310x150Logo.scale-400.png and /dev/null differ diff --git a/public/site.webmanifest b/public/site.webmanifest deleted file mode 100644 index 2ee79d1..0000000 --- a/public/site.webmanifest +++ /dev/null @@ -1,465 +0,0 @@ -{ - "name": "E-Cosplay", - "short_name": "E-Cosplay", - "description" : "Bienvenue dans la communauté e-cosplay ! Votre référence pour les concours, ateliers de craft, et l'entraide. Le cosplay est pour tous, rejoignez notre passion !", - "icons": [ - { - "src": "/favicon/windows11/SmallTile.scale-100.png", - "sizes": "71x71" - }, - { - "src": "/favicon/windows11/SmallTile.scale-125.png", - "sizes": "89x89" - }, - { - "src": "/favicon/windows11/SmallTile.scale-150.png", - "sizes": "107x107" - }, - { - "src": "/favicon/windows11/SmallTile.scale-200.png", - "sizes": "142x142" - }, - { - "src": "/favicon/windows11/SmallTile.scale-400.png", - "sizes": "284x284" - }, - { - "src": "/favicon/windows11/Square150x150Logo.scale-100.png", - "sizes": "150x150" - }, - { - "src": "/favicon/windows11/Square150x150Logo.scale-125.png", - "sizes": "188x188" - }, - { - "src": "/favicon/windows11/Square150x150Logo.scale-150.png", - "sizes": "225x225" - }, - { - "src": "/favicon/windows11/Square150x150Logo.scale-200.png", - "sizes": "300x300" - }, - { - "src": "/favicon/windows11/Square150x150Logo.scale-400.png", - "sizes": "600x600" - }, - { - "src": "/favicon/windows11/Wide310x150Logo.scale-100.png", - "sizes": "310x150" - }, - { - "src": "/favicon/windows11/Wide310x150Logo.scale-125.png", - "sizes": "388x188" - }, - { - "src": "/favicon/windows11/Wide310x150Logo.scale-150.png", - "sizes": "465x225" - }, - { - "src": "/favicon/windows11/Wide310x150Logo.scale-200.png", - "sizes": "620x300" - }, - { - "src": "/favicon/windows11/Wide310x150Logo.scale-400.png", - "sizes": "1240x600" - }, - { - "src": "/favicon/windows11/LargeTile.scale-100.png", - "sizes": "310x310" - }, - { - "src": "/favicon/windows11/LargeTile.scale-125.png", - "sizes": "388x388" - }, - { - "src": "/favicon/windows11/LargeTile.scale-150.png", - "sizes": "465x465" - }, - { - "src": "/favicon/windows11/LargeTile.scale-200.png", - "sizes": "620x620" - }, - { - "src": "/favicon/windows11/LargeTile.scale-400.png", - "sizes": "1240x1240" - }, - { - "src": "/favicon/windows11/Square44x44Logo.scale-100.png", - "sizes": "44x44" - }, - { - "src": "/favicon/windows11/Square44x44Logo.scale-125.png", - "sizes": "55x55" - }, - { - "src": "/favicon/windows11/Square44x44Logo.scale-150.png", - "sizes": "66x66" - }, - { - "src": "/favicon/windows11/Square44x44Logo.scale-200.png", - "sizes": "88x88" - }, - { - "src": "/favicon/windows11/Square44x44Logo.scale-400.png", - "sizes": "176x176" - }, - { - "src": "/favicon/windows11/StoreLogo.scale-100.png", - "sizes": "50x50" - }, - { - "src": "/favicon/windows11/StoreLogo.scale-125.png", - "sizes": "63x63" - }, - { - "src": "/favicon/windows11/StoreLogo.scale-150.png", - "sizes": "75x75" - }, - { - "src": "/favicon/windows11/StoreLogo.scale-200.png", - "sizes": "100x100" - }, - { - "src": "/favicon/windows11/StoreLogo.scale-400.png", - "sizes": "200x200" - }, - { - "src": "/favicon/windows11/SplashScreen.scale-100.png", - "sizes": "620x300" - }, - { - "src": "/favicon/windows11/SplashScreen.scale-125.png", - "sizes": "775x375" - }, - { - "src": "/favicon/windows11/SplashScreen.scale-150.png", - "sizes": "930x450" - }, - { - "src": "/favicon/windows11/SplashScreen.scale-200.png", - "sizes": "1240x600" - }, - { - "src": "/favicon/windows11/SplashScreen.scale-400.png", - "sizes": "2480x1200" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-16.png", - "sizes": "16x16" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-20.png", - "sizes": "20x20" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-24.png", - "sizes": "24x24" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-30.png", - "sizes": "30x30" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-32.png", - "sizes": "32x32" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-36.png", - "sizes": "36x36" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-40.png", - "sizes": "40x40" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-44.png", - "sizes": "44x44" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-48.png", - "sizes": "48x48" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-60.png", - "sizes": "60x60" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-64.png", - "sizes": "64x64" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-72.png", - "sizes": "72x72" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-80.png", - "sizes": "80x80" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-96.png", - "sizes": "96x96" - }, - { - "src": "/favicon/windows11/Square44x44Logo.targetsize-256.png", - "sizes": "256x256" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-16.png", - "sizes": "16x16" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-20.png", - "sizes": "20x20" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-24.png", - "sizes": "24x24" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-30.png", - "sizes": "30x30" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-32.png", - "sizes": "32x32" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-36.png", - "sizes": "36x36" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-40.png", - "sizes": "40x40" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-44.png", - "sizes": "44x44" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-48.png", - "sizes": "48x48" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-60.png", - "sizes": "60x60" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-64.png", - "sizes": "64x64" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-72.png", - "sizes": "72x72" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-80.png", - "sizes": "80x80" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-96.png", - "sizes": "96x96" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-unplated_targetsize-256.png", - "sizes": "256x256" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png", - "sizes": "16x16" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png", - "sizes": "20x20" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png", - "sizes": "24x24" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png", - "sizes": "30x30" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png", - "sizes": "32x32" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png", - "sizes": "36x36" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png", - "sizes": "40x40" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png", - "sizes": "44x44" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png", - "sizes": "48x48" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png", - "sizes": "60x60" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png", - "sizes": "64x64" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png", - "sizes": "72x72" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png", - "sizes": "80x80" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png", - "sizes": "96x96" - }, - { - "src": "/favicon/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png", - "sizes": "256x256" - }, - { - "src": "/favicon/android/android-launchericon-512-512.png", - "sizes": "512x512" - }, - { - "src": "/favicon/android/android-launchericon-192-192.png", - "sizes": "192x192" - }, - { - "src": "/favicon/android/android-launchericon-144-144.png", - "sizes": "144x144" - }, - { - "src": "/favicon/android/android-launchericon-96-96.png", - "sizes": "96x96" - }, - { - "src": "/favicon/android/android-launchericon-72-72.png", - "sizes": "72x72" - }, - { - "src": "/favicon/android/android-launchericon-48-48.png", - "sizes": "48x48" - }, - { - "src": "/favicon/ios/16.png", - "sizes": "16x16" - }, - { - "src": "/favicon/ios/20.png", - "sizes": "20x20" - }, - { - "src": "/favicon/ios/29.png", - "sizes": "29x29" - }, - { - "src": "/favicon/ios/32.png", - "sizes": "32x32" - }, - { - "src": "/favicon/ios/40.png", - "sizes": "40x40" - }, - { - "src": "/favicon/ios/50.png", - "sizes": "50x50" - }, - { - "src": "/favicon/ios/57.png", - "sizes": "57x57" - }, - { - "src": "/favicon/ios/58.png", - "sizes": "58x58" - }, - { - "src": "/favicon/ios/60.png", - "sizes": "60x60" - }, - { - "src": "/favicon/ios/64.png", - "sizes": "64x64" - }, - { - "src": "/favicon/ios/72.png", - "sizes": "72x72" - }, - { - "src": "/favicon/ios/76.png", - "sizes": "76x76" - }, - { - "src": "/favicon/ios/80.png", - "sizes": "80x80" - }, - { - "src": "/favicon/ios/87.png", - "sizes": "87x87" - }, - { - "src": "/favicon/ios/100.png", - "sizes": "100x100" - }, - { - "src": "/favicon/ios/114.png", - "sizes": "114x114" - }, - { - "src": "/favicon/ios/120.png", - "sizes": "120x120" - }, - { - "src": "/favicon/ios/128.png", - "sizes": "128x128" - }, - { - "src": "/favicon/ios/144.png", - "sizes": "144x144" - }, - { - "src": "/favicon/ios/152.png", - "sizes": "152x152" - }, - { - "src": "/favicon/ios/167.png", - "sizes": "167x167" - }, - { - "src": "/favicon/ios/180.png", - "sizes": "180x180" - }, - { - "src": "/favicon/ios/192.png", - "sizes": "192x192" - }, - { - "src": "/favicon/ios/256.png", - "sizes": "256x256" - }, - { - "src": "/favicon/ios/512.png", - "sizes": "512x512" - }, - { - "src": "/favicon/ios/1024.png", - "sizes": "1024x1024" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone", - "start_url": "https://www.e-cosplay.fr/", - "dir": "ltr", - "lang": "fr", - "orientation": "natural", - "categories": [ - "utilities" - ] -} diff --git a/public/sw.js b/public/sw.js index 00fc56c..b3222af 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,60 +1,599 @@ -// --- Service Worker pour la gestion des Notifications Push --- -// Permet au Service Worker de s'activer immédiatement après l'installation -self.addEventListener("message", (event) => { - if (event.data && event.data.type === "SKIP_WAITING") { - self.skipWaiting(); + +/**************************************************** WORKBOX IMPORT ****************************************************/ +// The configuration is set to use Workbox +// The following code will import Workbox from CDN or public URL +// Import from public URL + +importScripts('/workbox/workbox-sw.js'); +importScripts('/idb/umd.js'); +workbox.setConfig({modulePathPrefix: '/workbox'}); +/**************************************************** END WORKBOX IMPORT ****************************************************/ + + + +function registerCacheFirst(routeMatchFn, cacheName, plugins = []) { + const strategy = new workbox.strategies.CacheFirst({ cacheName, plugins }); + workbox.routing.registerRoute(routeMatchFn, strategy); + return strategy; +} + +function precacheResources(strategy, resourceList, event) { + if (!(event instanceof ExtendableEvent)) { + throw new Error("precacheResources needs a valid ExtendableEvent"); + } + return Promise.all(resourceList.map(path => + strategy.handleAll({ + event, + request: new Request(path), + })[1] + )); +} + +function createBackgroundSyncPlugin(queueName, maxRetentionTime = 2880, forceSyncFallback = false) { + return new workbox.backgroundSync.BackgroundSyncPlugin(queueName, { + maxRetentionTime, + forceSyncFallback + }); +} + +function createBackgroundSyncPluginWithBroadcast(queueName, channelName, maxRetentionTime = 2880, forceSyncFallback = false) { + const queue = new workbox.backgroundSync.Queue(queueName, { + maxRetentionTime, + forceSyncFallback, + }); + + const bc = new BroadcastChannel(channelName); + + const replayQueueWithProgress = async () => { + let entry; + let successCount = 0; + let failureCount = 0; + const total = (await queue.getAll()).length; + + while ((entry = await queue.shiftRequest())) { + try { + await fetch(entry.request.clone()); + successCount++; + } catch (error) { + failureCount++; + await queue.unshiftRequest(entry); + throw error; + } finally { + const remaining = (await queue.getAll()).length; + bc.postMessage({ name: queueName, replaying: true, remaining }); + } } + + const remaining = (await queue.getAll()).length; + bc.postMessage({ + name: queueName, + replayed: true, + remaining, + successCount, + failureCount, + }); + }; + + bc.onmessage = async (event) => { + if (event.data?.type === 'status-request') { + const entries = await queue.getAll(); + bc.postMessage({ name: queueName, remaining: entries.length }); + } + + if (event.data?.type === 'replay-request') { + try { + await replayQueueWithProgress(); + } catch (error) { + const entries = await queue.getAll(); + bc.postMessage({ + name: queueName, + replayed: false, + remaining: entries.length, + error: error.message, + }); + } + } + }; + + return { + fetchDidFail: async ({ request }) => { + await queue.pushRequest({ request }); + const entries = await queue.getAll(); + bc.postMessage({ name: queueName, remaining: entries.length }); + }, + onSync: async () => { + await replayQueueWithProgress(); + }, + }; +} + +const messageTasks = []; +function registerMessageTask(callback) { + messageTasks.push(callback); +} + +self.addEventListener('message', (event) => { + event.waitUntil( + messageTasks.reduce( + (chain, task) => chain.then(() => task(event)), + Promise.resolve() + ) + ); }); -// --- GESTION DES NOTIFICATIONS PUSH (Réception) --- +const installTasks = []; +function registerInstallTask(callback, priority = 100) { + installTasks.push({ + callback: (event) => { + const result = callback(event); + if (!result?.then) console.warn("Install task did not return a Promise"); + return result; + }, + priority, + }); +} +self.addEventListener('install', (event) => { + event.waitUntil( + installTasks + .sort((a, b) => a.priority - b.priority) + .reduce( + (chain, task) => chain.then(() => task.callback(event)), + Promise.resolve() + ) + ); +}); + +function statusGuard(min, max) { + return { + fetchDidSucceed: ({ response }) => { + if (response.status >= min && response.status <= max) { + throw new Error(`Server error: ${response.status}`); + } + return response; + } + }; +} + +registerMessageTask(async (event) => { + if (event.data?.type === 'SKIP_WAITING') { + await self.skipWaiting(); + } +}); + +const usedCacheNames = new Set(); +function registerCacheName(name) { + usedCacheNames.add(name); + return name; +} + +async function openBackgroundFetchDatabase() { + return await self.idb.openDB('bgfetch-completed', 1, { + upgrade(db) { + if (!db.objectStoreNames.contains('files')) { + db.createObjectStore('files', { keyPath: 'id' }); + } + + if (!db.objectStoreNames.contains('chunks')) { + const store = db.createObjectStore('chunks', { keyPath: ['id', 'index'] }); + store.createIndex('by-id', 'id'); + } else { + const store = db.transaction.objectStore('chunks'); + if (!Array.from(store.indexNames).includes('by-id')) { + store.createIndex('by-id', 'id'); + } + } + } + }); +} + +const bgFetchMetadata = new Map(); +const bgFetchChannel = new BroadcastChannel('bg-fetch'); +bgFetchChannel.onmessage = async (event) => { + const { type, id, meta } = event.data || {}; + + switch (type) { + case 'register-meta': + bgFetchMetadata.set(id, meta); + break; + + case 'get-meta': + const metadata = bgFetchMetadata.get(id) || null; + bgFetchChannel.postMessage({ type: 'meta-response', id, metadata }); + break; + + case 'clear-meta': + bgFetchMetadata.delete(id); + break; + + case 'list-stored-files': + { + const db = await openBackgroundFetchDatabase(); + const files = await db.getAll('files'); + bgFetchChannel.postMessage({ type: 'stored-files', files }); + break; + } + + case 'delete-stored-file': + { + const name = event.data.name; + const db = await openBackgroundFetchDatabase(); + const allFiles = await db.getAll('files'); + const target = allFiles.find(f => f.name === name); + if (target) { + await db.delete('files', target.id); + let index = 0; + while (await db.get('chunks', [target.id, index])) { + await db.delete('chunks', [target.id, index++]); + } + } + break; + } + } +}; + +const backgroundFetchTasks = { + click: [], + success: [], + fail: [], +}; +function registerBackgroundFetchTask(type, callback, priority = 100) { + if (!backgroundFetchTasks[type]) { + throw new Error(`Unknown background fetch event type: ${type}`); + } + + backgroundFetchTasks[type].push({ + callback: (event) => { + const result = callback(event); + if (!result?.then) { + console.warn(`[${type}] task did not return a Promise`); + } + return result; + }, + priority, + }); +} + +function runBackgroundFetchTasks(type, event) { + const tasks = backgroundFetchTasks[type] ?? []; + return tasks + .sort((a, b) => a.priority - b.priority) + .reduce( + (chain, task) => chain.then(() => task.callback(event)), + Promise.resolve() + ); +} + +self.addEventListener('backgroundfetchclick', (event) => { + event.waitUntil(runBackgroundFetchTasks('click', event)); +}); + +self.addEventListener('backgroundfetchsuccess', (event) => { + event.waitUntil(runBackgroundFetchTasks('success', event)); +}); + +self.addEventListener('backgroundfetchfail', (event) => { + event.waitUntil(runBackgroundFetchTasks('fail', event)); +}); + + +const pushTasks = []; +function registerPushTask(callback) { + pushTasks.push(callback); +} self.addEventListener('push', (event) => { - if (event.data) { - // Le payload JSON envoyé par votre serveur doit contenir - // 'title', 'message' et 'link'. - const data = event.data.json(); - - const title = data.title || 'Nouvelle Notification'; - const message = data.message || 'Contenu mis à jour.'; - const link = data.link || '/'; // Lien par défaut vers la racine - - const options = { - body: message, - // PATH MIS À JOUR ICI : Assurez-vous que l'icône est accessible - icon: data.icon || '/assets/notif.png', - data: { - link: link // On stocke le lien pour le réutiliser au clic - } - }; - - // Affiche la notification - event.waitUntil( - self.registration.showNotification(title, options) - ); + if (!(self.Notification && self.Notification.permission === 'granted')) { + return; } -}); - -// --- GESTION DES CLICS SUR LA NOTIFICATION --- -self.addEventListener('notificationclick', (event) => { - // Récupère le lien stocké dans la notification - const urlToOpen = event.notification.data.link || '/'; - - // Ferme la notification après le clic - event.notification.close(); - - // Ouvre l'URL associée, soit dans un onglet existant, soit dans un nouvel onglet event.waitUntil( - clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => { - - // Tente de trouver un client existant pour naviguer (si l'URL est déjà ouverte) - for (const client of clientList) { - if (client.url.includes(urlToOpen) && 'focus' in client) { - return client.focus(); - } - } - - // Sinon, ouvre une nouvelle fenêtre/onglet - return clients.openWindow(urlToOpen); - }) + pushTasks.reduce( + (chain, task) => chain.then(() => task(event)), + Promise.resolve() + ) ); }); + +const notificationActionHandlers = new Map(); +function registerNotificationAction(actionName, handler) { + notificationActionHandlers.set(actionName, handler); +} +self.addEventListener('notificationclick', event => { + event.notification.close(); + const action = event.action || ""; + const promises = []; + + const specificHandler = notificationActionHandlers.get(action); + if (typeof specificHandler === 'function') { + promises.push(Promise.resolve(specificHandler(event))); + } + + const wildcardHandler = notificationActionHandlers.get('*'); + if (typeof wildcardHandler === 'function') { + promises.push(Promise.resolve(wildcardHandler(event))); + } + + if (promises.length > 0) { + event.waitUntil(Promise.all(promises)); + } +}); +const structuredPushNotificationSupport = (event) => { + const {data} = event; + const sendNotification = response => { + const {title, options} = JSON.parse(response); + return self.registration.showNotification(title, options); + }; + + if (data) { + const message = data.text(); + event.waitUntil(sendNotification(message)); + } +} +function simplePushNotificationSupport(event) { + const { data } = event; + + if (!data) return; + + const message = data.text(); + const sendNotification = (text) => { + return self.registration.showNotification('Notification', { + body: text + }); + }; + + event.waitUntil(sendNotification(message)); +} + +const periodicSyncTasks = new Map(); + +function registerPeriodicSyncTask(tag, callback, priority = 100) { + if (!periodicSyncTasks.has(tag)) { + periodicSyncTasks.set(tag, []); + } + periodicSyncTasks.get(tag).push({ + priority, + callback: (event) => { + const result = callback(event); + if (!result?.then) console.warn(`[${tag}] periodic sync task did not return a Promise`); + return result; + } + }); +} + +async function runPeriodicSyncTasks(tag, event) { + const tasks = periodicSyncTasks.get(tag) ?? []; + return tasks + .sort((a, b) => a.priority - b.priority) + .reduce( + (chain, task) => chain.then(() => task.callback(event)), + Promise.resolve() + ); +} + +self.addEventListener('periodicsync', event => { + event.waitUntil(runPeriodicSyncTasks(event.tag, event)); +}); + +const periodicChannel = new BroadcastChannel('periodic-sync'); + +function notifyPeriodicSyncClients(tag, payload = {}) { + periodicChannel.postMessage({ + type: 'periodic-sync-update', + tag, + timestamp: Date.now(), + ...payload + }); +} + +const cacheInstances = new Map(); + +async function openCache(name) { + if (!cacheInstances.has(name)) { + const cache = await caches.open(name); + cacheInstances.set(name, cache); + } + return cacheInstances.get(name); +} + + + +/**************************************************** CACHE STRATEGY ****************************************************/ +// Strategy: CacheFirst +// Match: ({url}) => url.pathname.startsWith('/assets') +// Cache Name: assets +// Enabled: 1 +// Needs Workbox: 1 +// Method: + +// 1. Creation of the Workbox Cache Strategy object +// 2. Register the route with the Workbox Router +// 3. Add the assets to the cache when the service worker is installed + + +const cache_0_0 = new workbox.strategies.CacheFirst({ + cacheName: registerCacheName('assets'),plugins: [new workbox.expiration.ExpirationPlugin({ + "maxEntries": 68, + "maxAgeSeconds": 31536000 +})] +}); +workbox.routing.registerRoute(({url}) => url.pathname.startsWith('/assets'),cache_0_0); +registerInstallTask((event) => precacheResources(cache_0_0, [ + "/assets/@spomky-labs/pwa-bundle/badge_controller-fxsMWke.js", + "/assets/@spomky-labs/pwa-bundle/connection-status_controller-a6qV4RW.js", + "/assets/@spomky-labs/pwa-bundle/abstract_controller-lHossdH.js", + "/assets/@spomky-labs/pwa-bundle/capture_controller-yP__Kii.js", + "/assets/@spomky-labs/pwa-bundle/install_controller-TRkeV12.js", + "/assets/@spomky-labs/pwa-bundle/prefetch-on-demand_controller-EGnyR3S.js", + "/assets/@spomky-labs/pwa-bundle/fullscreen_controller-cDz4gEY.js", + "/assets/@spomky-labs/pwa-bundle/barcode-detection_controller-2wM_rT7.js", + "/assets/@spomky-labs/pwa-bundle/web-share_controller-WHj4CRa.js", + "/assets/@spomky-labs/pwa-bundle/file-handling_controller-F0Y1cZK.js", + "/assets/@spomky-labs/pwa-bundle/device-orientation_controller-IHb4jim.js", + "/assets/@spomky-labs/pwa-bundle/touch_controller-mAGtlT0.js", + "/assets/@spomky-labs/pwa-bundle/battery_controller-1zvUlM8.js", + "/assets/@spomky-labs/pwa-bundle/service-worker_controller-rj3_R_-.js", + "/assets/@spomky-labs/pwa-bundle/network-information_controller-a36uKfa.js", + "/assets/@spomky-labs/pwa-bundle/backgroundsync-queue_controller-GzsTYhv.js", + "/assets/@spomky-labs/pwa-bundle/device-motion_controller-Vn1KsVn.js", + "/assets/@spomky-labs/pwa-bundle/vibration_controller-WWNgRJt.js", + "/assets/@spomky-labs/pwa-bundle/backgroundsync-form_controller-gK5yd8x.js", + "/assets/@spomky-labs/pwa-bundle/web-push_controller-czf9Gth.js", + "/assets/@spomky-labs/pwa-bundle/geolocation_controller-eLT6hhX.js", + "/assets/@spomky-labs/pwa-bundle/jwt_signer-rMDxy1b.js", + "/assets/@spomky-labs/pwa-bundle/speech-synthesis_controller-P10zBDz.js", + "/assets/@spomky-labs/pwa-bundle/presentation_controller-Ja4LWIU.js", + "/assets/@spomky-labs/pwa-bundle/receiver_controller-auggO9d.js", + "/assets/@spomky-labs/pwa-bundle/background-fetch_controller-RRspbNT.js", + "/assets/@spomky-labs/pwa-bundle/wake-lock_controller-AZuUNNo.js", + "/assets/@spomky-labs/pwa-bundle/picture-in-picture_controller-BzBogPR.js", + "/assets/@spomky-labs/pwa-bundle/helpers-jYq9oR_.js", + "/assets/@spomky-labs/pwa-bundle/index-2g1hp95.js", + "/assets/@spomky-labs/pwa-bundle/contact_controller--bz1Lhb.js", + "/assets/PaymentForm-9z-bqNC.js", + "/assets/admin-EgoTwn3.js", + "/assets/app-NCj0QLm.js" +], event)); +/**************************************************** END CACHE STRATEGY ****************************************************/ + + + + + +/**************************************************** CACHE STRATEGY ****************************************************/ +// Strategy: CacheFirst +// Match: ({request}) => request.destination === 'font' +// Cache Name: fonts +// Enabled: 1 +// Needs Workbox: 1 +// Method: GET + +// 1. Creation of the Workbox Cache Strategy object +// 2. Register the route with the Workbox Router +// 3. Add the assets to the cache when the service worker is installed + + + + +const cache_2_0 = new workbox.strategies.CacheFirst({ + cacheName: registerCacheName('fonts'),plugins: [new workbox.cacheableResponse.CacheableResponsePlugin({ + "statuses": [ + 0, + 200 + ] +}), new workbox.expiration.ExpirationPlugin({ + "maxEntries": 60, + "maxAgeSeconds": 31536000 +})] +}); +workbox.routing.registerRoute(({request}) => request.destination === 'font',cache_2_0,'GET'); +/**************************************************** END CACHE STRATEGY ****************************************************/ + + + + + +/**************************************************** CACHE STRATEGY ****************************************************/ +// Strategy: StaleWhileRevalidate +// Match: ({url}) => url.origin === 'https://fonts.googleapis.com' +// Cache Name: google-fonts-stylesheets +// Enabled: 1 +// Needs Workbox: 1 +// Method: + +// 1. Creation of the Workbox Cache Strategy object +// 2. Register the route with the Workbox Router +// 3. Add the assets to the cache when the service worker is installed + + +const cache_3_0 = new workbox.strategies.StaleWhileRevalidate({ + cacheName: registerCacheName('google-fonts-stylesheets'),plugins: [] +}); +workbox.routing.registerRoute(({url}) => url.origin === 'https://fonts.googleapis.com',cache_3_0); +/**************************************************** END CACHE STRATEGY ****************************************************/ + + + + + +/**************************************************** CACHE STRATEGY ****************************************************/ +// Strategy: CacheFirst +// Match: ({url}) => url.origin === 'https://fonts.gstatic.com' +// Cache Name: google-fonts-webfonts +// Enabled: 1 +// Needs Workbox: 1 +// Method: + +// 1. Creation of the Workbox Cache Strategy object +// 2. Register the route with the Workbox Router +// 3. Add the assets to the cache when the service worker is installed + + + + +const cache_3_1 = new workbox.strategies.CacheFirst({ + cacheName: registerCacheName('google-fonts-webfonts'),plugins: [new workbox.cacheableResponse.CacheableResponsePlugin({ + "statuses": [ + 0, + 200 + ] +}), new workbox.expiration.ExpirationPlugin({ + "maxEntries": 30, + "maxAgeSeconds": 31536000 +})] +}); +workbox.routing.registerRoute(({url}) => url.origin === 'https://fonts.gstatic.com',cache_3_1); +/**************************************************** END CACHE STRATEGY ****************************************************/ + + + + + +/**************************************************** CACHE STRATEGY ****************************************************/ +// Strategy: CacheFirst +// Match: ({request, url}) => (request.destination === 'image' && !url.pathname.startsWith('/assets')) +// Cache Name: images +// Enabled: 1 +// Needs Workbox: 1 +// Method: + +// 1. Creation of the Workbox Cache Strategy object +// 2. Register the route with the Workbox Router +// 3. Add the assets to the cache when the service worker is installed + + +const cache_4_0 = new workbox.strategies.CacheFirst({ + cacheName: registerCacheName('images'),plugins: [] +}); +workbox.routing.registerRoute(({request, url}) => (request.destination === 'image' && !url.pathname.startsWith('/assets')),cache_4_0); +/**************************************************** END CACHE STRATEGY ****************************************************/ + + + + +/**************************************************** CACHE CLEAR ****************************************************/ +// The configuration is set to clear the cache on each install event +// The following code will remove all the caches +registerInstallTask(() => + caches.keys().then(keys => + Promise.all( + keys + .filter(k => usedCacheNames.has(k)) + .map(k => caches.delete(k)) + ) + ) +, 0); +/**************************************************** END CACHE CLEAR ****************************************************/ + + + + + +/**************************************************** SKIP WAITING ****************************************************/ +// The configuration is set to skip waiting on each install event +registerInstallTask(() => self.skipWaiting(), 5); +self.addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()); +}); +/**************************************************** END SKIP WAITING ****************************************************/ + + + diff --git a/src/Twig/ViteAssetExtension.php b/src/Twig/ViteAssetExtension.php index 52088ba..b48b24f 100644 --- a/src/Twig/ViteAssetExtension.php +++ b/src/Twig/ViteAssetExtension.php @@ -6,24 +6,20 @@ use Detection\MobileDetect; use Psr\Cache\CacheItemPoolInterface; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; +use Nelmio\SecurityBundle\EventListener\ContentSecurityPolicyListener; class ViteAssetExtension extends AbstractExtension { - // Clé réservée dans le manifest Vite pour le HTML généré des favicons. - const FAVICON_MANIFEST_KEY = '_FAVICONS_HTML_'; - - private ?array $manifestData = null; - const CACHE_KEY = 'vite_manifest'; - + private ?array $manifestData = null; private readonly bool $isDev; public function __construct( private readonly string $manifest, private readonly CacheItemPoolInterface $cache, + private readonly ContentSecurityPolicyListener $cspListener, ) { - // Respecte la logique existante : VITE_LOAD == "0" est considéré comme DEV. - $this->isDev = $_ENV['VITE_LOAD'] == "0"; + $this->isDev = $_ENV['VITE_LOAD'] === "0"; } public function getFunctions(): array @@ -31,20 +27,25 @@ class ViteAssetExtension extends AbstractExtension return [ new TwigFunction('vite_asset', $this->asset(...), ['is_safe' => ['html']]), new TwigFunction('isMobile', $this->isMobile(...), ['is_safe' => ['html']]), - // Nouvelle fonction Twig pour inclure les liens de favicons new TwigFunction('vite_favicons', $this->favicons(...), ['is_safe' => ['html']]) ]; } - public function isMobile() + /** + * Récupère le nonce pour les scripts via le Listener de Nelmio + */ + private function getNonce(): string + { + // Dans la v3.8, on utilise getNonce('script') sur le listener + return $this->cspListener->getNonce('script'); + } + + public function isMobile(): bool { $detect = new MobileDetect(); return $detect->isMobile() || $detect->isTablet(); } - /** - * Charge le manifeste s'il n'est pas déjà chargé et met en cache. - */ private function loadManifest(): void { if ($this->manifestData === null) { @@ -53,106 +54,61 @@ class ViteAssetExtension extends AbstractExtension $this->manifestData = $item->get(); } else { if (!file_exists($this->manifest)) { - // En cas d'erreur de fichier, initialise à un tableau vide $this->manifestData = []; return; } - $this->manifestData = json_decode((string)file_get_contents($this->manifest), true); - - if (json_last_error() !== JSON_ERROR_NONE) { - $this->manifestData = []; - } - $item->set($this->manifestData); $this->cache->save($item); } } } - // --- Gestion des assets JS/CSS (non modifiée) --- - - public function asset(string $entry, array $deps): string + public function asset(string $entry, array $deps = []): string { - if ($this->isDev) { - return $this->assetDev($entry, $deps); - } - - return $this->assetProd($entry); + return $this->isDev ? $this->assetDev($entry, $deps) : $this->assetProd($entry); } public function assetDev(string $entry, array $deps): string { - $html = << - HTML; - return $html . << - HTML; + $nonce = $this->getNonce(); + return << + +HTML; } public function assetProd(string $entry): string { $this->loadManifest(); + $nonce = $this->getNonce(); $file = $this->manifestData[$entry]['file'] ?? ''; $css = $this->manifestData[$entry]['css'] ?? []; - $imports = $this->manifestData[$entry]['imports'] ?? []; $html = << - HTML; + +HTML; foreach ($css as $cssFile) { - $html .= << - HTML; - } - - foreach ($imports as $import) { - $import = str_replace("_vendor","vendor",$import); - $import = str_replace("_turbo","turbo",$import); - $html .= << - HTML; + $html .= ''; } return $html; } - // --- Nouvelle Gestion des Favicons --- - public function favicons(): string { - if ($this->isDev) { - return $this->faviconsDev(); - } - - return $this->faviconsProd(); + return $this->isDev ? '' : $this->faviconsProd(); } - public function faviconsDev(): string - { - // En mode dev, on assume qu'un fichier favicon.ico ou favicon.png - // standard est présent dans le répertoire public. - return << - HTML; - } - - public function faviconsProd(): string + private function faviconsProd(): string { $this->loadManifest(); - - // Récupère le bloc HTML complet généré par le plugin dans le manifest. - // On suppose que l'entrée est un tableau associatif avec la clé 'html'. - $faviconData = $this->manifestData; $faviconHtml = ""; - foreach ($faviconData as $key =>$favicon) { - if(!str_contains($key,".js")) { - $faviconHtml .= << - HTML; + foreach ($this->manifestData as $key => $favicon) { + if(!str_contains($key, ".js") && isset($favicon['file'])) { + $faviconHtml .= ''; } } return $faviconHtml; diff --git a/symfony.lock b/symfony.lock index f605b87..d895b3d 100644 --- a/symfony.lock +++ b/symfony.lock @@ -100,6 +100,18 @@ "config/packages/nelmio_cors.yaml" ] }, + "nelmio/security-bundle": { + "version": "3.8", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.4", + "ref": "71045833e4f882ad9de8c95fe47efb99a1eec2f7" + }, + "files": [ + "config/packages/nelmio_security.yaml" + ] + }, "phpstan/phpstan": { "version": "2.1", "recipe": { @@ -154,6 +166,18 @@ "config/packages/sentry.yaml" ] }, + "spomky-labs/pwa-bundle": { + "version": "1.4", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "1.3", + "ref": "ac33d9498ab25856f16d1d7df9ad4b9b0cad066d" + }, + "files": [ + "config/packages/pwa.yaml" + ] + }, "stripe/stripe-php": { "version": "19.0", "recipe": { diff --git a/templates/base.twig b/templates/base.twig index 539b9fb..8fa28b3 100644 --- a/templates/base.twig +++ b/templates/base.twig @@ -56,6 +56,8 @@ {% block breadcrumb_schema %}{% endblock %} {{ vite_asset('app.js', []) }} + + {{ pwa(swAttributes={ 'nonce': csp_nonce('script') }) }}