PageSourceSearch

https://www.solution-financement.com/service-worker-push.js

js solution-financement.com collected 2026-10-07 22:40:29 UTC 6,405 bytes, 158 lines download raw bytes

1const PREFIX = "V1.10";
2const CACHED_FILES = ['favicon.ico','./css_generic/fonts/Roboto/Roboto.woff2','./offline.html'];
3// Optimisation : Set pour recherche O(1) au lieu de Array O(n) si la liste grandit
4const MAP_CACHED_FILES = ['GandG_efoodage', 'GandG_ecommerce', 'GandG_simple', 'global--gestion-commande'];
5
6let unreadNotif = 0;
7
8/** Gestion des notifications **/
9self.addEventListener('push', (event)=> {
10    if (!(self.Notification && self.Notification.permission === 'granted')) return;
11
12    if (event.data) {
13        const message = event.data.text();
14        const objectToDisplay = JSON.parse(message);
15
16        // 2FA number matching : 3 boutons d'action (le choix est POSTé par notificationclick).
17        // Clic sur le corps (iOS/Firefox sans boutons) → data.link = page d'approbation (repli).
18        if (objectToDisplay.type === '2fa') {
19            const actions = (objectToDisplay.numbers || []).map(n => ({ action: 'gg2fa_' + n, title: String(n) }));
20            event.waitUntil(self.registration.showNotification(objectToDisplay.title, {
21                body: objectToDisplay.msg,
22                tag: 'gg2fa_' + objectToDisplay.pushId, // un seul challenge affiché à la fois
23                renotify: true,
24                requireInteraction: true,               // ne disparaît pas tout seul pendant la saisie
25                actions,
26                data: {
27                    gg2fa: {
28                        pushId: objectToDisplay.pushId,
29                        token: objectToDisplay.token,
30                        apiUrl: objectToDisplay.apiUrl,
31                        msgOk: objectToDisplay.msgOk,
32                        msgKo: objectToDisplay.msgKo
33                    },
34                    link: objectToDisplay.link
35                },
36                icon: 'apple-touch-icon.png'
37            }));
38            return;
39        }
40
41        const promiseChain = self.registration.showNotification(objectToDisplay.title, {
42            body: objectToDisplay.msg,
43            data: { link: objectToDisplay.link },
44            icon: 'apple-touch-icon.png'
45        });
46
47        event.waitUntil(promiseChain);
48        
49        // Gestion badge
50        if ('setAppBadge' in self.navigator) {
51             // Idéalement, récupérer le vrai compte depuis le serveur, ici on incrémente juste localement
52             // unreadNotif est défini globalement ? Attention au redémarrage du SW qui remet à 0.
53             // Pour l'instant on laisse tel quel.
54            event.waitUntil(self.navigator.setAppBadge(unreadNotif)); 
55        }
56    }
57});
58
59self.addEventListener('notificationclick', (event) => {
60    event.notification.close();
61    const data = event.notification.data || {};
62
63    // 2FA : clic sur un des 3 nombres → POST du choix (jeton usage unique = preuve)
64    if (data.gg2fa && event.action && event.action.indexOf('gg2fa_') === 0) {
65        const choice = event.action.slice(6);
66        event.waitUntil((async () => {
67            try {
68                const apiUrl = new URL(data.gg2fa.apiUrl);
69                if (apiUrl.origin !== self.location.origin) return; // jamais de POST hors du site
70                const resp = await fetch(apiUrl.href, {
71                    method: 'POST',
72                    body: new URLSearchParams({ pushId: data.gg2fa.pushId, token: data.gg2fa.token, choice })
73                });
74                await self.registration.showNotification(event.notification.title, {
75                    body: resp.ok ? (data.gg2fa.msgOk || 'OK') : (data.gg2fa.msgKo || 'KO'),
76                    tag: 'gg2fa_result',
77                    icon: 'apple-touch-icon.png'
78                });
79            } catch (e) {
80                console.warn('2FA push approve failed', e);
81            }
82        })());
83        return;
84    }
85
86    const link = data.link;
87
88    event.waitUntil((async () => {
89        try {
90            const urlObj = new URL(link);
91            if (['http:', 'https:'].includes(urlObj.protocol)) {
92                await clients.openWindow(urlObj.href);
93            } else {
94                console.warn('Protocole invalide:', link);
95            }
96        } catch (e) {
97            await clients.openWindow('/');
98        }
99    })());
100});
101
102self.addEventListener("install", (event) => {
103    self.skipWaiting();
104    console.log(`${PREFIX} Install`);
105    event.waitUntil((async ()=>{
106        const cache = await caches.open(PREFIX);
107        // Robustesse : on essaie de tout mettre en cache, mais on ne plante pas si un fichier manque
108        await Promise.all(
109            CACHED_FILES.map(url => cache.add(url).catch(e => console.error(`Echec cache ${url}`, e)))
110        );
111    })());
112});
113
114self.addEventListener('activate', (event) => {
115    event.waitUntil(self.clients.claim());
116    console.log(`${PREFIX} Activate`);
117    
118    event.waitUntil((async () => {
119        if (self.registration.navigationPreload) {
120            await self.registration.navigationPreload.enable();
121        }
122        const keys = await caches.keys();
123        await Promise.all(
124            keys.map(key => {
125                if (!key.includes(PREFIX)) return caches.delete(key);
126            })
127        );
128    })());
129});
130
131self.addEventListener("fetch", (event) => {
132    if (event.request.mode === "navigate") {
133        event.respondWith((async () => {
134            try {
135                const preloadResponse = await event.preloadResponse;
136                if (preloadResponse) return preloadResponse;
137                return await fetch(event.request);
138            } catch (error) {
139                const cache = await caches.open(PREFIX);
140                return await cache.match('./offline.html');
141            }
142        })());
143    } else {
144        const shouldCache = MAP_CACHED_FILES.some(file => event.request.url.includes(file));
145        if (shouldCache) {
146            event.respondWith((async () => {
147                const cache = await caches.open(PREFIX);
148                try {
149                    const response = await fetch(event.request);
150                    cache.put(event.request, response.clone());
151                    return response;
152                } catch (e) {
153                    return await cache.match(event.request);
154                }
155            })());
156        }
157    }
158});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.