import { defineNuxtPlugin, useCookie, useRuntimeConfig } from 'nuxt/app'; import type { Pinia } from 'pinia'; import useConfig from '../composables/useConfig.ts'; import { longtimeCookieSetting } from '../src/cookieSettings.ts'; import { isGrantedForUser, parseUserJwt } from '../src/helpers.ts'; import type { Account, User } from '../src/user.ts'; import { useMainStore } from '../store/index.ts'; import type { PermissionArea } from '~/src/helpers.ts'; declare module '#app' { interface NuxtApp { $user(): User | null; $isGranted(area?: string, locale?: string | null): boolean; $accounts(): Promise; $setToken(token: string | null): Promise; $removeToken(username?: string | null): Promise; } } declare module 'vue' { interface ComponentCustomProperties { $user(): User | null; $isGranted(area?: string, locale?: string | null): boolean; $accounts(): Promise; $setToken(token: string | null): Promise; $removeToken(username?: string | null): Promise; } } export default defineNuxtPlugin(async (nuxtApp) => { const runtimeConfig = useRuntimeConfig(); const config = useConfig(); const store = useMainStore(nuxtApp.$pinia as Pinia); const tokenCookie = useCookie('token', longtimeCookieSetting); if (tokenCookie.value) { await store.setToken(tokenCookie.value); if (!store.token) { tokenCookie.value = null; } } const user = () => store.user; const isGranted = (area: PermissionArea = '', locale = null): boolean => { return !!store.user && !!store.user.authenticated && isGrantedForUser(store.user, locale || config.locale, area) ; }; const getAccounts = async (fallback: string | null = null): Promise> => { const tokens = (window.localStorage.getItem('account-tokens') || fallback || '').split('|').filter((x) => !!x); const accounts: Record = {}; for (const token of tokens) { const account = await parseUserJwt(token, runtimeConfig.public.publicKey, config.locale); if (account !== null && account.username && account.authenticated) { accounts[account.username] = { token, account }; } } return accounts; }; const saveAccounts = (accounts: Record): void => { store.setAccounts(accounts); window.localStorage.setItem('account-tokens', Object.values(accounts).map((x) => x.token) .join('|')); }; const accounts = async (): Promise => { saveAccounts(await getAccounts(store.token)); }; const setToken = async (token: string | null): Promise => { const accounts = await getAccounts(); const usernameBefore = store.user?.username; tokenCookie.value = token; await store.setToken(token); if (store.user !== null && store.token !== null) { if (store.user.username && store.user.authenticated) { accounts[store.user.username] = { token: store.token, account: store.user }; } } else { tokenCookie.value = null; } saveAccounts(accounts); const usernameAfter = store.user?.username; if (usernameBefore !== usernameAfter) { const bc = new BroadcastChannel('account_switch'); bc.postMessage(usernameAfter); bc.close(); } }; const removeToken = async (username: string | null = null): Promise => { const accounts = await getAccounts(); if (store.user) { delete accounts[username || store.user.username]; } if (!username) { if (Object.keys(accounts).length === 0) { await store.setToken(null); tokenCookie.value = null; } else { await store.setToken(Object.values(accounts)[0].token); tokenCookie.value = store.token; } } saveAccounts(accounts); }; return { provide: { user, isGranted, accounts, setToken, removeToken, }, }; });