// Importing necessary libraries and components const axios = require('axios'); // Axios for HTTP requests import Vue from 'vue'; // Vue.js framework import App from './App.vue'; // Main Vue component import './registerServiceWorker'; // Service worker registration import router from '@/router'; // Application router import store from '@/store'; // Vuex store for state management import * as Sentry from '@sentry/vue'; // Importing CSS for styling import './assets/styles/base.css'; // Base styles import './assets/resources/semantic-ui-2.4.2/semantic.min.css'; // Semantic UI for UI components import '@fortawesome/fontawesome-free/css/all.css'; // Font Awesome for icons import '@fortawesome/fontawesome-free/js/all.js'; // Font Awesome JS import 'ol/ol.css'; // OpenLayers CSS for maps import '@/assets/styles/openlayers-custom.css'; // Custom styles for OpenLayers import '@/assets/styles/sidebar-layers.css'; // Styles for sidebar layers // Font Awesome library setup import { library } from '@fortawesome/fontawesome-svg-core'; import { fas } from '@fortawesome/free-solid-svg-icons'; // Importing solid icons import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; // Font Awesome component // Vue Multiselect CSS import 'vue-multiselect/dist/vue-multiselect.min.css'; // Multiselect component styles Sentry.init({ Vue, dsn: 'https://de982b53ff2a58de08749f46c3f7f830@sentry.neogeo.fr/32', integrations: [ Sentry.browserTracingIntegration({ router }), Sentry.replayIntegration(), ], // Tracing tracesSampleRate: 1.0, // Capture 100% of the transactions // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled tracePropagationTargets: ['localhost', /^https:\/\/yourserver\.io\/api/], // Session Replay replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production. replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur. beforeSend(event) { console.log('Event about to be sent to Sentry:', event); return event; } }); // Adding Font Awesome icons to the library library.add(fas); // Registering Font Awesome as a Vue component for use in templates Vue.component('FontAwesomeIcon', FontAwesomeIcon); // Setting Vue's production tip configuration Vue.config.productionTip = false; Vue.config.ignoredElements = ['geor-header']; // Handling service worker updates and precaching var refreshing = false; // Flag to prevent multiple refreshes if (navigator.serviceWorker) { navigator.serviceWorker.addEventListener('controllerchange', () => { // Check if the page is already refreshing to prevent duplicate refreshes if (refreshing) { return; } refreshing = true; // Reload the page to activate the new service worker window.location.reload(); }); } /** * Dynamically loads a font from Google Fonts and sets CSS variables. * @param {string} fontNames - A comma-separated list of font names, where the first font is the one to be imported and others are fallbacks. * @param {string} headerColor - The color to be used for headers. * @param {string} primaryColor - The primary color for the application. * @param {string} primaryHighlightColor - The primary color to highlight elements in the application. */ const setAppTheme = (fontNames, headerColor, primaryColor, primaryHighlightColor) => { // Set CSS variables for header and primary color. if (headerColor) { document.documentElement.style.setProperty('--header-color', headerColor); } if (primaryColor) { document.documentElement.style.setProperty('--primary-color', primaryColor); } if (primaryHighlightColor) { document.documentElement.style.setProperty('--primary-highlight-color', primaryHighlightColor); } // Proceed to load the font if fontNames is provided. if (fontNames) { const fontNameToImport = fontNames.split(',')[0].trim(); const link = document.createElement('link'); link.href = `https://fonts.googleapis.com/css?family=${fontNameToImport.replace(/ /g, '+')}:400,700&display=swap`; link.rel = 'stylesheet'; document.head.appendChild(link); // Set the CSS variable for font family. document.documentElement.style.setProperty('--font-family', fontNames); } }; /** * Sets the favicon of the application. * @param {string} favicoUrl - The URL of the favicon to be set. */ const setFavicon = (favicoUrl) => { const link = document.createElement('link'); link.id = 'dynamic-favicon'; link.rel = 'shortcut icon'; link.href = favicoUrl; document.head.appendChild(link); }; /** * Regularly updates the online status of the application. */ const updateOnlineStatus = () => { setInterval(() => { store.commit('SET_IS_ONLINE', navigator.onLine); }, 2000); }; /** * Regularly updates the user status if using external auth to keep the frontend updated with backend. */ function handleLogout() { if (store.state.user) { store.commit('SET_USER', false); store.commit('SET_USER_PERMISSIONS', null); store.commit('SET_USER_LEVEL_PROJECTS', null); store.commit('DISPLAY_MESSAGE', { level: 'negative', comment: `Vous avez été déconnecté du service d'authentification. Reconnectez-vous ou continuez en mode anonyme.` }); store.dispatch('projects/GET_PROJECTS'); store.dispatch('GET_USER_LEVEL_PERMISSIONS'); store.dispatch('GET_USER_LEVEL_PROJECTS'); } } const updateUserStatus = () => { setInterval(() => { if (navigator.onLine) { axios .get(`${store.state.configuration.VUE_APP_DJANGO_API_BASE}user_info/`) .then((response) => { const user = response.data?.user || null; // Cas où l'utilisateur a changé if (store.state.user?.username !== user.username) { store.commit('SET_USER', user); // Cas où l'utilisateur est bien authentifié if (user) { store.commit('DISPLAY_MESSAGE', { level: 'positive', comment: 'Bienvenue à nouveau ! Vous êtes reconnecté au service d\'authentification' }); store.dispatch('projects/GET_PROJECTS'); store.dispatch('GET_USER_LEVEL_PERMISSIONS'); store.dispatch('GET_USER_LEVEL_PROJECTS'); } else { // On force la suppression de l'utilisateur au cas où le serveur SSO ne permet pas à la requête API d'aboutir (ex: redirection si non authentifié SSO) handleLogout(); } } }) .catch(() => { handleLogout(); }); } }, 10000); }; /** * Fetches initial data for the application and initializes the Vue instance. */ const fetchDataAndInitializeApp = async () => { await Promise.all([ store.dispatch('GET_USER_INFO'), store.dispatch('GET_STATIC_PAGES'), store.dispatch('map/GET_AVAILABLE_LAYERS'), store.dispatch('GET_LEVELS_PERMISSIONS'), store.dispatch('GET_PROJECT_ATTRIBUTES'), ]); new Vue({ router, store, render: h => h(App) }).$mount('#app'); }; /** * Initializes the application configuration. * @param {object} config - Configuration object with application settings. */ const onConfigLoaded = async (config) => { // Set application configuration in the store. store.commit('SET_CONFIG', config); // Update the online status at regular intervals. updateOnlineStatus(); // Update the user status at regular intervals to check if backend session expired. updateUserStatus(); // Set the document title and favicon from the configuration. document.title = `${config.VUE_APP_APPLICATION_NAME} ${config.VUE_APP_APPLICATION_ABSTRACT}`; setFavicon(config.VUE_APP_APPLICATION_FAVICO); // Apply the application theme settings using values specified in the configuration. setAppTheme( config.VUE_APP_FONT_FAMILY, config.VUE_APP_HEADER_COLOR, config.VUE_APP_PRIMARY_COLOR, config.VUE_APP_PRIMARY_HIGHLIGHT_COLOR ); // Set a global proxy URL based on the configuration. window.proxy_url = config.VUE_APP_DJANGO_API_BASE + 'proxy/'; // Fetch initial data and initialize the Vue application. await fetchDataAndInitializeApp(); }; // Attempt to load the application configuration from an external JSON file. axios.get('./config/config.json') .catch((error) => { // Log an error if the configuration file cannot be loaded. console.error(error); console.log('Attempting to get config from Localstorage'); // Attempt to retrieve the configuration from local storage as a fallback. const conf = localStorage.getItem('geontrib_conf'); if (conf) { // If a configuration is found in local storage, parse it and load the config. onConfigLoaded(JSON.parse(conf)); } }) .then((response) => { // Check if the response is valid and the request was successful. if (response && response.status === 200) { // Store the retrieved configuration in local storage for future use. localStorage.setItem('geontrib_conf', JSON.stringify(response.data)); // Load the configuration into the application. onConfigLoaded(response.data); } }) .catch((error) => { // Throw an error if there are issues processing the response. throw error; });