PageSourceSearch

https://droneskycheck-d0136.web.app/js/core/account/account-service.js?v=__VERSION__

js droneskycheck-d0136.web.app collected 2026-10-03 12:22:10 UTC 14,666 bytes, 449 lines download raw bytes

1(function () {
2  "use strict";
3
4  window.DSC = window.DSC || {};
5  const root = window.DSC.Core = window.DSC.Core || window.DSC_CORE || {};
6  window.DSC_CORE = root;
7
8  function getContext() {
9    return root.getContext?.() || root.context || {};
10  }
11
12  function log(message, data) {
13    root.log?.debug?.("ACCOUNT", message, data);
14  }
15
16  let unsubscribeAuthState = null;
17  let authReady = false;
18
19  function getAuth() {
20    try {
21      return root.firebase?.authService || root.firebase?.auth?.() || null;
22    } catch (error) {
23      root.log?.warn?.("ACCOUNT", "Firebase Auth unavailable", error);
24      return null;
25    }
26  }
27
28  function getGoogleProvider() {
29    const sdk = root.firebase?.sdk || window.firebase || null;
30    const Provider = sdk?.auth?.GoogleAuthProvider;
31    return typeof Provider === "function" ? new Provider() : null;
32  }
33
34  function trimText(value) {
35    return typeof value === "string" ? value.trim() : "";
36  }
37
38  function normalizeEmail(email) {
39    return trimText(email).toLowerCase();
40  }
41
42  function getSearchParams() {
43    try {
44      return new URLSearchParams(window.location.search);
45    } catch {
46      return new URLSearchParams();
47    }
48  }
49
50  function hasSearchValue(name, expectedValue = null) {
51    const value = getSearchParams().get(name);
52    if (expectedValue === null) return trimText(value) !== "";
53    return value === expectedValue;
54  }
55
56  function isSyncLoginPage() {
57    const pathname = String(window.location.pathname || "").toLocaleLowerCase("it-IT");
58    return pathname.endsWith("/sync-login.html") || pathname === "/sync-login.html";
59  }
60
61  function isCloudAccountAccessAllowed() {
62    // Formazione can restore an existing account; DSC+ still requires membership.
63    const formationPage = ["/formazione/", "/formazione/index.html"].includes(window.location.pathname);
64    return formationPage || hasSearchValue("dscplus", "beta") ||
65      hasSearchValue("invite") ||
66      hasSearchValue("admin", "true") ||
67      hasSearchValue("controlcenter", "true") ||
68      isSyncLoginPage();
69  }
70
71  function getCloudAccountAccessDisabledMessage() {
72    return "Accesso DSC+ disponibile solo dal link beta o da un invito DSC+.";
73  }
74
75  function createAccessDisabledError() {
76    const error = new Error(getCloudAccountAccessDisabledMessage());
77    error.code = "auth/cloud-account-access-disabled";
78    return error;
79  }
80
81  function requireCloudAccountAccess() {
82    if (!isCloudAccountAccessAllowed()) {
83      throw createAccessDisabledError();
84    }
85  }
86
87  function getProvider(user) {
88    const providers = Array.isArray(user?.providerData)
89      ? user.providerData.map(provider => provider?.providerId).filter(Boolean)
90      : [];
91    return providers[0] || "password";
92  }
93
94  function normalizeUser(user) {
95    if (!user) return null;
96
97    return {
98      uid: user.uid || null,
99      email: user.email || null,
100      emailVerified: user.emailVerified === true,
101      displayName: user.displayName || null,
102      provider: getProvider(user),
103      status: "authenticated"
104    };
105  }
106
107  function getAnonymousAccountState(context = getContext()) {
108    return {
109      authenticated: false,
110      identity: null,
111      email: null,
112      emailVerified: false,
113      displayName: null,
114      provider: null,
115      status: authReady ? "anonymous" : "loading"
116    };
117  }
118
119  async function updateAuthContext(user) {
120    const identity = normalizeUser(user);
121    if (typeof root.updateContext !== "function") return identity;
122    const previousUid = trimText(getContext()?.identity?.uid);
123    const nextUid = trimText(identity?.uid);
124    const scopeChanged = previousUid !== nextUid;
125    const scopedContext = scopeChanged
126      ? root.repositoryManager?.switchIdentityScope?.(nextUid || null)
127      : null;
128    const nextContext = {
129      ...(scopeChanged && scopedContext ? scopedContext : getContext()),
130      authenticated: identity !== null,
131      identity,
132      membership: null,
133      membershipLoading: identity !== null,
134      features: []
135    };
136    if (scopeChanged && typeof root.setContext === "function") root.setContext(nextContext);
137    else root.updateContext(nextContext);
138
139    if (typeof root.membership?.syncMembershipForIdentity === "function") {
140      await root.membership.syncMembershipForIdentity(identity);
141    }
142
143    root.events?.emit?.("account:changed", service.getAccountState());
144    return identity;
145  }
146
147  function createAuthError(error, fallback) {
148    const code = error?.code || "";
149    const messages = {
150      "auth/email-already-in-use": "Questa email risulta gia registrata. Prova ad accedere oppure usa il recupero password.",
151      "auth/invalid-email": "L'indirizzo email non e valido.",
152      "auth/missing-email": "Inserisci l'indirizzo email.",
153      "auth/user-disabled": "Questo account e stato disabilitato.",
154      "auth/user-not-found": "Non esiste nessun account con questa email.",
155      "auth/wrong-password": "La password non e corretta.",
156      "auth/invalid-credential": "Email o password non corretti.",
157      "auth/invalid-login-credentials": "Email o password non corretti.",
158      "auth/weak-password": "La password e troppo debole. Usa almeno 6 caratteri.",
159      "auth/missing-password": "Inserisci la password.",
160      "auth/network-request-failed": "Connessione assente o instabile. Riprova quando sei online.",
161      "auth/too-many-requests": "Troppi tentativi ravvicinati. Attendi qualche minuto e riprova.",
162      "auth/operation-not-allowed": "Accesso email/password non abilitato nella configurazione Firebase.",
163      "auth/requires-recent-login": "Per questa operazione serve un accesso recente. Esci e accedi di nuovo.",
164      "auth/user-token-expired": "La sessione e scaduta. Accedi di nuovo.",
165      "auth/account-exists-with-different-credential": "Esiste gia un account con questa email usando un metodo di accesso diverso.",
166      "auth/cancelled-popup-request": "Operazione annullata.",
167      "auth/popup-blocked": "Il popup di accesso e stato bloccato dal browser.",
168      "auth/popup-closed-by-user": "Operazione annullata."
169    };
170
171    const message = messages[code] || fallback || "Si e verificato un errore Firebase. Riprova piu tardi.";
172    const authError = new Error(message);
173    authError.code = code || "auth/unknown";
174    authError.originalError = error || null;
175    return authError;
176  }
177
178  async function requireAuth() {
179    const auth = getAuth();
180    if (!auth) {
181      throw createAuthError(null, "Autenticazione Firebase non disponibile.");
182    }
183    return auth;
184  }
185
186  async function requireCurrentUser() {
187    const auth = await requireAuth();
188    const user = auth.currentUser || null;
189    if (!user) {
190      throw createAuthError({ code: "auth/user-not-found" }, "Devi accedere prima di eseguire questa operazione.");
191    }
192    return user;
193  }
194
195  function getSafeInsightsReturnTo() {
196    try {
197      const current = new URL(window.location.href);
198      if (current.pathname !== "/" && current.pathname !== "/index.html") return null;
199      const requested = current.searchParams.get("returnTo");
200      if (!requested) return null;
201      const target = new URL(requested, current.origin);
202      return target.origin === current.origin &&
203        target.pathname === "/admin/insights.html" &&
204        target.search === "" &&
205        target.hash === ""
206        ? target.pathname
207        : null;
208    } catch {
209      return null;
210    }
211  }
212
213  function returnToInsightsAfterAuthentication() {
214    const target = getSafeInsightsReturnTo();
215    if (!target) return false;
216    if (typeof window.location.assign === "function") window.location.assign(target);
217    else window.location.href = target;
218    return true;
219  }
220
221  async function handleAuthState(user) {
222    authReady = true;
223    try {
224      if (user && !isCloudAccountAccessAllowed()) {
225        try {
226          await getAuth()?.signOut?.();
227        } catch (signOutError) {
228          root.log?.warn?.("ACCOUNT", "blocked auth sign out failed", signOutError);
229        }
230        await updateAuthContext(null);
231        log("auth state blocked outside DSC+ access URL");
232        return;
233      }
234
235      await updateAuthContext(user || null);
236      if (user) returnToInsightsAfterAuthentication();
237      log("auth state changed", normalizeUser(user));
238    } catch (error) {
239      root.log?.warn?.("ACCOUNT", "auth context update failed", error);
240    }
241  }
242
243  async function handleAuthStateError(error) {
244    authReady = true;
245    root.log?.warn?.("ACCOUNT", "auth state listener failed", error);
246    try {
247      await updateAuthContext(null);
248    } catch (updateError) {
249      root.log?.warn?.("ACCOUNT", "auth context reset failed", updateError);
250    }
251  }
252
253  const service = {
254    init() {
255      const auth = getAuth();
256      if (!auth || typeof auth.onAuthStateChanged !== "function") {
257        authReady = true;
258        log("auth unavailable");
259        return this;
260      }
261
262      if (!unsubscribeAuthState && root.firebaseAuthReady?.subscribe) {
263        unsubscribeAuthState = root.firebaseAuthReady.subscribe(authState => {
264          if (authState?.status === "AUTH_LOADING") return;
265          if (authState?.status === "AUTH_ERROR") {
266            void handleAuthStateError(authState.error);
267            return;
268          }
269          void handleAuthState(authState?.user || null);
270        });
271        root.firebaseAuthReady.start?.();
272      } else if (!unsubscribeAuthState) {
273        unsubscribeAuthState = auth.onAuthStateChanged(handleAuthState, handleAuthStateError);
274      }
275
276      log("service ready");
277      return this;
278    },
279
280    isAuthAvailable() {
281      return getAuth() !== null;
282    },
283
284    isAuthReady() {
285      return authReady === true;
286    },
287
288    isAuthenticated() {
289      return getContext().authenticated === true;
290    },
291
292    isCloudAccountAccessAllowed,
293
294    getCloudAccountAccessDisabledMessage,
295
296    getSafeInsightsReturnTo,
297
298    getAccountState() {
299      const context = getContext();
300      const identity = context.identity || null;
301      const account = identity ? {
302        authenticated: context.authenticated === true,
303        identity,
304        email: identity.email || null,
305        emailVerified: identity.emailVerified === true,
306        displayName: identity.displayName || null,
307        provider: identity.provider || null,
308        status: context.membershipLoading === true ? "loading" : (context.authenticated === true ? "authenticated" : "anonymous"),
309        plan: context.membership?.plan || null
310      } : getAnonymousAccountState(context);
311
312      return {
313        ...account,
314        authenticated: context.authenticated === true,
315        pilotProfile: context.pilotProfile || null,
316        operator: context.operator || null,
317        workspace: context.workspace || null,
318        membership: context.membership || null,
319        features: Array.isArray(context.features) ? [...context.features] : []
320      };
321    },
322
323    async registerWithEmail(email, password, options = {}) {
324      requireCloudAccountAccess();
325      const auth = await requireAuth();
326      const normalizedEmail = normalizeEmail(email);
327      const displayName = trimText(options.displayName);
328
329      if (!normalizedEmail) {
330        throw createAuthError({ code: "auth/invalid-email" });
331      }
332
333      try {
334        const credential = await auth.createUserWithEmailAndPassword(normalizedEmail, password);
335        const user = credential?.user || auth.currentUser;
336
337        if (user && displayName && typeof user.updateProfile === "function") {
338          await user.updateProfile({ displayName });
339        }
340
341        if (user && typeof user.sendEmailVerification === "function") {
342          await user.sendEmailVerification();
343        }
344
345        if (user && typeof user.reload === "function") {
346          await user.reload();
347        }
348
349        await updateAuthContext(auth.currentUser || user || null);
350        return this.getAccountState();
351      } catch (error) {
352        throw createAuthError(error, "Registrazione non riuscita.");
353      }
354    },
355
356    async loginWithEmail(email, password) {
357      requireCloudAccountAccess();
358      const auth = await requireAuth();
359      const normalizedEmail = normalizeEmail(email);
360
361      if (!normalizedEmail) {
362        throw createAuthError({ code: "auth/invalid-email" });
363      }
364
365      try {
366        await auth.signInWithEmailAndPassword(normalizedEmail, password);
367        await updateAuthContext(auth.currentUser || null);
368        return this.getAccountState();
369      } catch (error) {
370        throw createAuthError(error, "Accesso non riuscito.");
371      }
372    },
373
374    async loginWithGoogle() {
375      requireCloudAccountAccess();
376      const auth = await requireAuth();
377      const provider = getGoogleProvider();
378
379      if (!provider || typeof auth.signInWithPopup !== "function") {
380        throw createAuthError(null, "Accesso Google non disponibile nella configurazione Firebase.");
381      }
382
383      try {
384        await auth.signInWithPopup(provider);
385        await updateAuthContext(auth.currentUser || null);
386        return this.getAccountState();
387      } catch (error) {
388        throw createAuthError(error, "Accesso Google non riuscito.");
389      }
390    },
391
392    async logout() {
393      const auth = await requireAuth();
394
395      try {
396        await auth.signOut();
397        await updateAuthContext(null);
398        return this.getAccountState();
399      } catch (error) {
400        throw createAuthError(error, "Logout non riuscito.");
401      }
402    },
403
404    async sendPasswordReset(email) {
405      requireCloudAccountAccess();
406      const auth = await requireAuth();
407      const normalizedEmail = normalizeEmail(email);
408
409      if (!normalizedEmail) {
410        throw createAuthError({ code: "auth/invalid-email" });
411      }
412
413      try {
414        await auth.sendPasswordResetEmail(normalizedEmail);
415        return true;
416      } catch (error) {
417        throw createAuthError(error, "Invio del recupero password non riuscito.");
418      }
419    },
420
421    async sendEmailVerification() {
422      const user = await requireCurrentUser();
423
424      try {
425        await user.sendEmailVerification();
426        return true;
427      } catch (error) {
428        throw createAuthError(error, "Invio della verifica email non riuscito.");
429      }
430    },
431
432    async refreshAuthState() {
433      const auth = await requireAuth();
434      const user = auth.currentUser || null;
435
436      try {
437        if (user && typeof user.reload === "function") {
438          await user.reload();
439        }
440        await updateAuthContext(auth.currentUser || null);
441        return this.getAccountState();
442      } catch (error) {
443        throw createAuthError(error, "Aggiornamento dello stato account non riuscito.");
444      }
445    }
446  };
447
448  root.account = service;
449})();

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.