diff --git a/src/App.vue b/src/App.vue
index 1e865e99..2771cb35 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -2,11 +2,14 @@
import { useAppStore } from 'stores/app';
import { useUserStore } from 'stores/user';
import { onBeforeMount } from 'vue';
+import { useRouter } from 'vue-router';
+
const appStore = useAppStore();
const userStore = useUserStore();
+const router = useRouter();
onBeforeMount(async () => {
- await userStore.init();
+ await userStore.init(router);
await appStore.init();
});
diff --git a/src/composables/usePrintService.js b/src/composables/usePrintService.js
index e96e26e2..05077083 100644
--- a/src/composables/usePrintService.js
+++ b/src/composables/usePrintService.js
@@ -5,8 +5,7 @@ import { useQuasar } from 'quasar';
export function usePrintService() {
const quasar = useQuasar();
- const userStore = useUserStore();
- const token = userStore.token;
+ const { getTokenMultimedia } = useUserStore();
function sendEmail(path, params) {
return axios.post(path, params).then(() =>
@@ -18,17 +17,17 @@ export function usePrintService() {
);
}
- function openReport(path, params) {
+ function openReport(path, params, isNewTab = '_self') {
+ if (typeof params === 'string') params = JSON.parse(params);
params = Object.assign(
{
- access_token: token
+ access_token: getTokenMultimedia()
},
params
);
const query = new URLSearchParams(params).toString();
-
- window.open(`api/${path}?${query}`);
+ window.open(`api/${path}?${query}`, isNewTab);
}
return {
diff --git a/src/i18n/ca-ES/index.js b/src/i18n/ca-ES/index.js
index 41f2a1a8..0a3d7b1a 100644
--- a/src/i18n/ca-ES/index.js
+++ b/src/i18n/ca-ES/index.js
@@ -73,7 +73,7 @@ export default {
Reports: 'Informes',
Configuration: 'Configuració',
Shelves: 'Prestatgeries',
- Account: 'Compte',
+ Account: 'Configuració',
Addresses: 'Adreces',
OrderSummary: 'Resum de la comanda',
Checkout: `Configurar l'encarrec`,
@@ -162,5 +162,10 @@ export default {
ErrCantWrite: "No s'ha pogut escriure el fitxer al disc",
ErrExtension: "La pujada del fitxer s'ha aturat per una extensió",
ErrDefault: 'Error de pujada desconegut',
- 'Sync complete': 'Sincronització completa'
+ 'Sync complete': 'Sincronització completa',
+ // Errors
+ errors: {
+ statusUnauthorized: 'Accés denegat',
+ tokenConfig: 'Error al obtenir la configuració del token'
+ }
};
diff --git a/src/i18n/en-US/index.js b/src/i18n/en-US/index.js
index 47a25a50..b18d90cc 100644
--- a/src/i18n/en-US/index.js
+++ b/src/i18n/en-US/index.js
@@ -85,7 +85,7 @@ export default {
Reports: 'Reports',
Configuration: 'Configuration',
Shelves: 'Shelves',
- Account: 'Account',
+ Account: 'Configuration',
Addresses: 'Addresses',
OrderSummary: 'Order summary',
Checkout: 'Configure order',
@@ -195,5 +195,10 @@ export default {
ErrCantWrite: 'Failed to write file to disk',
ErrExtension: 'File upload stopped by extension',
ErrDefault: 'Unknown upload error',
- 'Sync complete': 'Synchronization complete'
+ 'Sync complete': 'Synchronization complete',
+ // Errors
+ errors: {
+ statusUnauthorized: 'Access denied',
+ tokenConfig: 'Error fetching token config'
+ }
};
diff --git a/src/i18n/es-ES/index.js b/src/i18n/es-ES/index.js
index d1fbdd7a..dda8dab9 100644
--- a/src/i18n/es-ES/index.js
+++ b/src/i18n/es-ES/index.js
@@ -67,11 +67,11 @@ export default {
'Pending orders': 'Pedidos pendientes',
'Last orders': 'Pedidos confirmados',
Invoices: 'Facturas',
- Basket: 'Cesta',
+ Basket: 'Cesta de la compra',
Catalog: 'Catálogo',
Administration: 'Administración',
'Control panel': 'Panel de control',
- Users: 'Usuarios',
+ Users: 'Gestión de usuarios',
Connections: 'Conexiones',
Visits: 'Visitas',
News: 'Gestión de noticias',
@@ -82,7 +82,7 @@ export default {
Reports: 'Informes',
Configuration: 'Configuración',
Shelves: 'Estanterías',
- Account: 'Cuenta',
+ Account: 'Configuración',
Addresses: 'Direcciones',
OrderSummary: 'Resumen del pedido',
Checkout: 'Configurar pedido',
@@ -108,7 +108,7 @@ export default {
Connections: 'Conexiones',
Visits: 'Visitas',
News: 'Noticias',
- Photos: 'Imágenes',
+ Photos: 'Fotos',
Items: 'Artículos',
Account: 'Cuenta',
Addresses: 'Direcciones'
@@ -194,5 +194,10 @@ export default {
ErrCantWrite: 'Failed to write file to disk',
ErrExtension: 'File upload stopped by extension',
ErrDefault: 'Unknown upload error',
- 'Sync complete': 'Sincronización completada'
+ 'Sync complete': 'Sincronización completada',
+ // Errors
+ errors: {
+ statusUnauthorized: 'Acceso denegado',
+ tokenConfig: 'Error al obtener configuración de token'
+ }
};
diff --git a/src/i18n/fr-FR/index.js b/src/i18n/fr-FR/index.js
index 57590f43..56a5742e 100644
--- a/src/i18n/fr-FR/index.js
+++ b/src/i18n/fr-FR/index.js
@@ -73,7 +73,7 @@ export default {
Reports: 'Rapports',
Configuration: 'Configuration',
Shelves: 'Étagères',
- Account: 'Compte',
+ Account: 'Configuration',
Addresses: 'Adresses',
OrderSummary: 'Résumé de la commande',
Checkout: 'Configurer la commande',
@@ -166,5 +166,11 @@ export default {
ErrCantWrite: "Échec de l'écriture du fichier sur le disque",
ErrExtension: 'Téléchargement du fichier arrêté par extension',
ErrDefault: 'Erreur de téléchargement inconnue',
- 'Sync complete': 'Synchronisation terminée'
+ 'Sync complete': 'Synchronisation terminée',
+ // Errors
+ errors: {
+ statusUnauthorized: 'Accès refusé',
+ tokenConfig:
+ 'Erreur lors de la récupération de la configuration du jeton'
+ }
};
diff --git a/src/i18n/pt-PT/index.js b/src/i18n/pt-PT/index.js
index 18b5cdc2..f49db9f4 100644
--- a/src/i18n/pt-PT/index.js
+++ b/src/i18n/pt-PT/index.js
@@ -72,7 +72,7 @@ export default {
Reports: 'Informes',
Configuration: 'Configuração',
Shelves: 'Estantes',
- Account: 'Conta',
+ Account: 'Configuração',
Addresses: 'Moradas',
OrderSummary: 'Resumo da encomenda',
Checkout: 'Configurar encomenda',
@@ -159,5 +159,10 @@ export default {
ErrCantWrite: 'Erro ao gravar arquivo no disco',
ErrExtension: 'Erro de extensão do arquivo',
ErrDefault: 'Erro desconhecido ao subir arquivo',
- 'Sync complete': 'Sincronização completa'
+ 'Sync complete': 'Sincronização completa',
+ // Errors
+ errors: {
+ statusUnauthorized: 'Acesso negado',
+ tokenConfig: 'Erro ao obter configuração do token'
+ }
};
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 94daa6f8..ef2f762e 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -1,5 +1,5 @@
@@ -113,9 +122,7 @@ onMounted(() => fetchLanguagesSql());
option-label="name"
option-value="code"
:options="langOptions"
- @update:model-value="
- userStore.updateUserLang(data.lang)
- "
+ @update:model-value="updateConfigLang(data.lang)"
/>
diff --git a/src/pages/Ecomerce/BasketView.vue b/src/pages/Ecomerce/BasketView.vue
index d9206d39..3fefc59b 100644
--- a/src/pages/Ecomerce/BasketView.vue
+++ b/src/pages/Ecomerce/BasketView.vue
@@ -104,21 +104,27 @@ const fetchData = async () => {
:to="{ name: 'checkout', params: { id: orderId } }"
rounded
no-caps
- />
+ >
+ {{ t('configureOrder') }}
+
+ >
+ {{ t('catalog') }}
+
+ >
+ {{ t('checkout') }}
+
{
JOIN vn.itemTypeL10n l ON l.id = t.id
WHERE t.order >= 0
AND t.categoryFk = #category
- ORDER BY t.order, l.name;
+ ORDER BY t.order, l.name ASC;
DROP TEMPORARY TABLE tmp.itemAvailable`,
{
category: selectedCategory.value,
@@ -650,7 +650,7 @@ const getItemColors = async () => {
JOIN tmp.itemAvailable a ON a.id = i.id
JOIN vn.inkL10n l ON l.id = i.inkFk
WHERE (${queryFilter.value})
- ORDER BY name;
+ ORDER BY name ASC;
DROP TEMPORARY TABLE tmp.itemAvailable;`,
{
orderId: basketOrderId.value
@@ -683,7 +683,7 @@ const getProducers = async () => {
JOIN tmp.itemAvailable a ON a.id = i.id
JOIN vn.producer p ON p.id = i.producerFk
WHERE (${queryFilter.value})
- ORDER BY name;
+ ORDER BY name ASC;
DROP TEMPORARY TABLE tmp.itemAvailable;`,
{ orderId: basketOrderId.value }
);
@@ -704,7 +704,7 @@ const getOrigins = async () => {
JOIN vn.origin o ON o.id = i.originFk
JOIN vn.originL10n l ON l.id = o.id
WHERE (${queryFilter.value})
- ORDER BY name;
+ ORDER BY name ASC;
DROP TEMPORARY TABLE tmp.itemAvailable;`,
{ orderId: basketOrderId.value }
);
@@ -723,7 +723,7 @@ const getSubcategories = async () => {
JOIN vn.itemType t ON t.id = i.typeFk
JOIN tmp.itemAvailable a ON a.id = i.id
WHERE (${queryFilter.value})
- ORDER BY category;
+ ORDER BY category ASC;
DROP TEMPORARY TABLE tmp.itemAvailable;`,
{ orderId: basketOrderId.value }
);
diff --git a/src/pages/Ecomerce/TicketView.vue b/src/pages/Ecomerce/TicketView.vue
index a164514a..b04dad63 100644
--- a/src/pages/Ecomerce/TicketView.vue
+++ b/src/pages/Ecomerce/TicketView.vue
@@ -5,17 +5,16 @@ import { useI18n } from 'vue-i18n';
import TicketDetails from 'src/pages/Ecomerce/TicketDetails.vue';
-import { useUserStore } from 'stores/user';
import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia';
+import { usePrintService } from 'src/composables/usePrintService';
const { t } = useI18n();
const jApi = inject('jApi');
const route = useRoute();
-const userStore = useUserStore();
const appStore = useAppStore();
const { isHeaderMounted } = storeToRefs(appStore);
-const { user, token } = storeToRefs(userStore);
+const { openReport } = usePrintService();
const ticket = ref({});
const rows = ref([]);
@@ -38,16 +37,8 @@ onMounted(async () => {
);
});
-const onPrintClick = () => {
- const params = new URLSearchParams({
- access_token: token.value,
- recipientId: user.value.id,
- type: 'deliveryNote'
- });
- window.open(
- `/api/Tickets/${ticket.value.id}/delivery-note-pdf?${params.toString()}`
- );
-};
+const onPrintClick = () =>
+ openReport(`Tickets/${ticket.value.id}/delivery-note-pdf`, {}, '_blank');
diff --git a/src/pages/Login/LoginView.vue b/src/pages/Login/LoginView.vue
index f4fd6e1d..07b10b1c 100644
--- a/src/pages/Login/LoginView.vue
+++ b/src/pages/Login/LoginView.vue
@@ -5,14 +5,18 @@ import { useI18n } from 'vue-i18n';
import { useUserStore } from 'stores/user';
import useNotify from 'src/composables/useNotify.js';
import { useRouter, useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import { useAppStore } from 'src/stores/app';
const { notify } = useNotify();
const { t } = useI18n();
const { locale } = useI18n({ useScope: 'global' });
-
const userStore = useUserStore();
+const appStore = useAppStore();
const route = useRoute();
const router = useRouter();
+const { siteLang, localeOptions } = storeToRefs(appStore);
+
const email = ref(null);
const password = ref(null);
const remember = ref(false);
@@ -20,12 +24,11 @@ const showPwd = ref(false);
const selectedLocaleValue = computed({
get() {
- return userStore.localeOptions.find(
- option => option.lang === locale.value
- ).value;
+ return siteLang.value;
},
set(value) {
locale.value = value;
+ appStore.updateSiteLocale(value);
}
});
@@ -44,7 +47,6 @@ onMounted(() => {
const onLogin = async () => {
await userStore.fetchUser();
- await userStore.updateUserLang(selectedLocaleValue.value);
await router.push('/');
};
@@ -90,7 +92,7 @@ const loginAsGuest = async () => {
/>
{
if (from.name === to.name) return;
const app = useAppStore();
+
app.$patch({
- title: i18n.global.t(
- to.meta.title ? `titles.${to.meta.title}` : 'titles.Home'
- ),
+ title: to.meta.title || 'Home',
subtitle: null,
useRightDrawer: false,
rightDrawerOpen: true
diff --git a/src/stores/app.js b/src/stores/app.js
index 8a856ad2..a7b06564 100644
--- a/src/stores/app.js
+++ b/src/stores/app.js
@@ -6,6 +6,7 @@ import { useQuasar } from 'quasar';
const { notify } = useNotify();
const storageOrderName = 'hederaBasket';
+const { t } = i18n.global;
export const useAppStore = defineStore('hedera', {
state: () => ({
@@ -16,15 +17,23 @@ export const useAppStore = defineStore('hedera', {
rightDrawerOpen: false,
isHeaderMounted: false,
menuEssentialLinks: [],
+ hiddenMenuLinks: new Set(['Reports']),
basketOrderId: null,
localeDates: {
days: [],
months: [],
daysShort: [],
monthsShort: []
- }
+ },
+ siteLang: null,
+ localeOptions: [
+ { label: t('langs.en'), lang: 'en-US', value: 'en' },
+ { label: t('langs.es'), lang: 'es-ES', value: 'es' },
+ { label: t('langs.ca'), lang: 'ca-ES', value: 'ca' },
+ { label: t('langs.fr'), lang: 'fr-FR', value: 'fr' },
+ { label: t('langs.pt'), lang: 'pt-PT', value: 'pt' }
+ ]
}),
-
actions: {
async getMenuLinks() {
const sections = await jApi.query('SELECT * FROM myMenu');
@@ -51,7 +60,6 @@ export const useAppStore = defineStore('hedera', {
this.menuEssentialLinks = sectionTree;
},
-
async loadConfig() {
const imageUrl = await jApi.getValue('SELECT url FROM imageConfig');
this.$patch({ imageUrl });
@@ -68,7 +76,7 @@ export const useAppStore = defineStore('hedera', {
},
async init() {
- // this.router.push({ name: 'login' });
+ this.updateSiteLocale(localStorage.getItem('siteLang') || 'es-ES');
this.getBasketOrderId();
this.getLocaleDates();
},
@@ -145,9 +153,24 @@ export const useAppStore = defineStore('hedera', {
onLogout() {
this.unloadOrder();
this.menuEssentialLinks = [];
+ },
+
+ updateSiteLocale(locale = null) {
+ const _locale = locale || 'es-ES';
+ i18n.global.locale.value = _locale;
+ this.siteLang = _locale;
+ localStorage.setItem('siteLang', _locale);
}
},
getters: {
+ filteredMenuItems: state => {
+ return (state.menuEssentialLinks || []).filter(
+ item => !state.hiddenMenuLinks.has(item.description)
+ );
+ },
+ siteLocaleOption: state =>
+ state.localeOptions.find(l => l.value === state.siteLang),
+ menuTitle: state => t(`titles.${state.title}`),
isMobile() {
const $q = useQuasar();
return $q?.screen?.width <= 768;
diff --git a/src/stores/user.js b/src/stores/user.js
index 5c347005..8ff2751c 100644
--- a/src/stores/user.js
+++ b/src/stores/user.js
@@ -1,146 +1,327 @@
import { defineStore } from 'pinia';
+import { ref, computed, watch } from 'vue';
import { api, jApi } from 'boot/axios';
-import { i18n } from 'src/boot/i18n';
import useNotify from 'src/composables/useNotify.js';
import { useAppStore } from 'src/stores/app.js';
-const { t } = i18n.global;
const { notify } = useNotify();
+const TOKEN_MULTIMEDIA = 'tokenMultimedia';
+const TOKEN = 'token';
-export const useUserStore = defineStore('user', {
- state: () => {
- const token =
- sessionStorage.getItem('vnToken') ||
- localStorage.getItem('vnToken');
+export const useUserStore = defineStore('user', () => {
+ const token = ref('');
+ const tokenMultimedia = ref('');
+ const isGuest = ref(false);
+ const user = ref(null); // Usuario en uso => supplantedUser || mainUser
+ const supplantedUser = ref(null); // Usuario suplantado
+ const mainUser = ref(null); // Usuario principal logueado
- return {
- token,
- isGuest: false,
- user: null,
- supplantedUser: null,
- localeOptions: [
- { label: t('langs.en'), lang: 'en-US', value: 'en' },
- { label: t('langs.es'), lang: 'es-ES', value: 'es' },
- { label: t('langs.ca'), lang: 'ca-ES', value: 'ca' },
- { label: t('langs.fr'), lang: 'fr-FR', value: 'fr' },
- { label: t('langs.pt'), lang: 'pt-PT', value: 'pt' }
- ]
- };
- },
+ const keepLogin = ref(false);
+ const intervalId = ref(null);
+ const isCheckingToken = ref(false);
+ const tokenConfig = ref(null);
+ let router;
- getters: {
- loggedIn: state => state.token != null,
- userLocaleOption: state =>
- state.localeOptions?.find(l => l.value === state?.user?.lang)
- },
+ const loggedIn = computed(() => !!token.value);
+ const storage = computed(() =>
+ keepLogin.value ? localStorage : sessionStorage
+ );
- actions: {
- async init() {
- this.isGuest = localStorage.getItem('hederaGuest') || false;
- const autoLoginStatus = await this.tryAutoLogin();
+ const init = async _router => {
+ router = _router;
+ isGuest.value = localStorage.getItem('hederaGuest') || false;
+ await getToken();
+ if (!loggedIn.value) {
+ const autoLoginStatus = await tryAutoLogin();
if (!autoLoginStatus) {
- this.router.push({ name: 'login' });
+ router.push({ name: 'login' });
}
- await this.fetchUser();
- await this.supplantInit();
- this.updateSiteLocale();
- },
-
- async getToken() {
- this.token =
- sessionStorage.getItem('vnToken') ||
- localStorage.getItem('vnToken');
- },
-
- async tryAutoLogin() {
- const guest = localStorage.getItem('hederaGuest');
-
- if (this.isGuest || guest) {
- localStorage.setItem('hederaGuest', true);
- return true;
- }
-
- if (!this.token) this.getToken();
-
- if (this.token) return true;
-
- return false;
- },
-
- async login(user, password, remember) {
- const params = { user, password };
- const res = await api.post('Accounts/login', params);
-
- if (remember) {
- localStorage.setItem('vnToken', res.data.token);
- } else {
- sessionStorage.setItem('vnToken', res.data.token);
- }
-
- this.$patch({
- token: res.data.token,
- name: user
- });
- },
-
- async logout() {
- if (this.token != null) {
- try {
- await api.post('Accounts/logout');
- } catch (e) {}
- localStorage.removeItem('vnToken');
- sessionStorage.removeItem('vnToken');
- }
- this.$reset();
- localStorage.removeItem('hederaGuest');
- useAppStore().onLogout();
- },
-
- async fetchUser(userType = 'user') {
- try {
- const userData = await jApi.getObject(
- 'SELECT id, nickname, name, lang FROM account.myUser'
- );
- this.$patch({ [userType]: userData });
- } catch (error) {
- console.error('Error fetching user: ', error);
- }
- },
-
- async supplantUser(supplantUser) {
- const json = await jApi.send('client/supplant', {
- supplantUser
- });
- this.token = json;
- sessionStorage.setItem('supplantUser', supplantUser);
- await this.fetchUser('supplantedUser');
- },
-
- async supplantInit() {
- const user = sessionStorage.getItem('supplantUser');
- if (user == null) return;
- await this.supplantUser(user);
- },
-
- async logoutSupplantedUser() {
- sessionStorage.removeItem('supplantUser');
- this.supplantedUser = null;
- await api.post('Accounts/logout');
- this.getToken();
- await this.fetchUser();
- },
-
- updateSiteLocale(locale = null) {
- i18n.global.locale.value =
- locale || this.userLocaleOption.lang || 'en-US';
- },
-
- async updateUserLang(lang) {
- if (!this.user || this.user.lang === lang) return;
-
- this.user.lang = lang;
- this.updateSiteLocale();
- notify(t('dataSaved'), 'positive');
}
- }
+ await fetchTokenConfig();
+ await fetchUser();
+ await supplantInit();
+ startInterval();
+ };
+
+ const getToken = () => {
+ const tokenValue =
+ sessionStorage.getItem(TOKEN) || localStorage.getItem(TOKEN);
+ token.value = tokenValue;
+ return tokenValue;
+ };
+
+ const getTokenMultimedia = () => {
+ const tokenMultimediaValue =
+ sessionStorage.getItem(TOKEN_MULTIMEDIA) ||
+ localStorage.getItem(TOKEN_MULTIMEDIA);
+ tokenMultimedia.value = tokenMultimediaValue;
+ return tokenMultimediaValue;
+ };
+
+ const setToken = ({ _token, _tokenMultimedia }) => {
+ storage.value.setItem(TOKEN, _token);
+ storage.value.setItem(TOKEN_MULTIMEDIA, _tokenMultimedia);
+ token.value = _token;
+ tokenMultimedia.value = _tokenMultimedia;
+ };
+
+ const destroyToken = async (url, storageType, key) => {
+ if (storageType.getItem(key)) {
+ try {
+ await api.post(url, null, {
+ headers: { Authorization: storageType.getItem(key) }
+ });
+ } catch (error) {
+ notify('errors.statusUnauthorized', 'negative');
+ } finally {
+ storageType.removeItem(key);
+ }
+ }
+ };
+
+ const destroy = async (destroyTokens = true) => {
+ const tokens = {
+ tokenMultimedia: 'Accounts/logout',
+ token: 'VnUsers/logout'
+ };
+ let destroyTokenPromises = [];
+ try {
+ if (destroyTokens) {
+ const { data: isValidToken } = await api.get(
+ 'VnUsers/validateToken'
+ );
+ if (isValidToken) {
+ destroyTokenPromises = Object.entries(tokens).map(
+ ([key, url]) => destroyToken(url, storage.value, key)
+ );
+ }
+ }
+ } finally {
+ localStorage.clear();
+ sessionStorage.clear();
+ await Promise.allSettled(destroyTokenPromises);
+ user.value = null;
+ stopRenewer();
+ }
+ };
+
+ const setSession = data => {
+ setToken({
+ _token: data.token,
+ _tokenMultimedia: data.tokenMultimedia
+ });
+ storage.value.setItem('hederaLastUser', data.username);
+ storage.value.setItem('created', data.created);
+ storage.value.setItem('ttl', data.ttl);
+ localStorage.setItem('keepLogin', keepLogin.value);
+ };
+
+ const fetchMultimediaToken = async data => {
+ const {
+ data: { multimediaToken }
+ } = await api.get('VnUsers/ShareToken', {
+ headers: { Authorization: data.token }
+ });
+ return multimediaToken;
+ };
+
+ const login = async (username, password, remember) => {
+ const params = { user: username, password };
+ const { data } = await api.post('Accounts/login', params);
+
+ const multimediaToken = await fetchMultimediaToken(data);
+ if (!multimediaToken) return;
+
+ keepLogin.value = remember;
+ setSession({
+ created: Date.now(),
+ tokenMultimedia: multimediaToken.id,
+ username,
+ ...data
+ });
+ await fetchTokenConfig();
+ startInterval();
+ };
+
+ const tryAutoLogin = async () => {
+ if (isGuest.value) {
+ localStorage.setItem('hederaGuest', true);
+ return true;
+ }
+
+ if (!token.value) getToken();
+
+ if (token.value) return true;
+
+ return false;
+ };
+
+ const logout = async () => {
+ try {
+ await api.post('Accounts/logout');
+ } catch (e) {}
+ destroy();
+ $reset();
+ useAppStore().onLogout();
+ };
+
+ const fetchTokenConfig = async () => {
+ try {
+ const { data } = await api.get('AccessTokenConfigs/findOne', {
+ filter: { fields: ['renewInterval', 'renewPeriod'] }
+ });
+ if (!data) return;
+ tokenConfig.value = data;
+ storage.value.setItem('renewPeriod', data.renewPeriod);
+ return data;
+ } catch (error) {
+ notify('errors.tokenConfig', 'negative');
+ console.error('Error fetching token config:', error);
+ }
+ };
+
+ const renewToken = async () => {
+ const _token = getToken();
+ const tokenData = await api.post('VnUsers/renewToken', {
+ headers: { Authorization: _token }
+ });
+
+ const _tokenMultimedia = getTokenMultimedia();
+ const tokenMultimedia = await api.post('VnUsers/renewToken', {
+ headers: { Authorization: _tokenMultimedia }
+ });
+
+ setToken({
+ _token: tokenData.data.id,
+ _tokenMultimedia: tokenMultimedia.data.id
+ });
+ };
+
+ const checkValidity = async () => {
+ const created = +storage.value.getItem('created');
+ const ttl = +storage.value.getItem('ttl');
+
+ if (isCheckingToken.value || !created) return;
+ isCheckingToken.value = true;
+
+ const renewPeriodInSeconds =
+ Math.min(ttl, tokenConfig.value?.renewPeriod) * 1000;
+ const maxDate = created + renewPeriodInSeconds;
+ const now = new Date().getTime();
+
+ if (isNaN(renewPeriodInSeconds) || now <= maxDate) {
+ isCheckingToken.value = false;
+ return;
+ }
+
+ await renewToken();
+ isCheckingToken.value = false;
+ };
+
+ const stopRenewer = () => {
+ clearInterval(intervalId.value);
+ };
+
+ const startInterval = () => {
+ stopRenewer();
+ const renewPeriod = +storage.value.getItem('renewPeriod');
+ if (!renewPeriod) return;
+ intervalId.value = setInterval(
+ () => checkValidity(),
+ renewPeriod * 1000
+ );
+ };
+
+ const fetchUser = async (userType = 'user') => {
+ try {
+ const userData = await jApi.getObject(
+ 'SELECT id, nickname, name, lang FROM account.myUser'
+ );
+ if (userType === 'user') mainUser.value = userData;
+ else supplantedUser.value = userData;
+ } catch (error) {
+ console.error('Error fetching user: ', error);
+ }
+ };
+
+ const supplantUser = async supplantUser => {
+ const json = await jApi.send('client/supplant', { supplantUser });
+ token.value = json;
+ sessionStorage.setItem('supplantUser', supplantUser);
+ await fetchUser('supplantedUser');
+ };
+
+ const supplantInit = async () => {
+ const user = sessionStorage.getItem('supplantUser');
+ if (!user) return;
+ await supplantUser(user);
+ };
+
+ const logoutSupplantedUser = async () => {
+ sessionStorage.removeItem('supplantUser');
+ supplantedUser.value = null;
+ await api.post('Accounts/logout');
+ getToken();
+ await fetchUser();
+ };
+
+ const updateUserLang = async lang => {
+ if (!user.value || user.value.lang === lang) return;
+ user.value.lang = lang;
+ };
+
+ const $reset = () => {
+ token.value = '';
+ isGuest.value = false;
+ user.value = null;
+ supplantedUser.value = null;
+ mainUser.value = null;
+ keepLogin.value = false;
+ intervalId.value = null;
+ isCheckingToken.value = false;
+ tokenConfig.value = null;
+ };
+
+ watch(
+ [mainUser, supplantedUser],
+ () => (user.value = supplantedUser.value || mainUser.value),
+
+ { immediate: true }
+ );
+
+ return {
+ token,
+ tokenMultimedia,
+ isGuest,
+ user,
+ supplantedUser,
+ mainUser,
+ keepLogin,
+ intervalId,
+ isCheckingToken,
+ tokenConfig,
+ loggedIn,
+ storage,
+ getToken,
+ getTokenMultimedia,
+ setToken,
+ destroyToken,
+ destroy,
+ setSession,
+ login,
+ tryAutoLogin,
+ logout,
+ fetchTokenConfig,
+ renewToken,
+ checkValidity,
+ stopRenewer,
+ startInterval,
+ fetchUser,
+ supplantUser,
+ supplantInit,
+ logoutSupplantedUser,
+ updateUserLang,
+ init,
+ $reset
+ };
});