2025-05-03 18:11:56 +02:00

124 lines
4.2 KiB
TypeScript

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<void>;
$setToken(token: string | null): Promise<void>;
$removeToken(username?: string | null): Promise<void>;
}
}
declare module 'vue' {
interface ComponentCustomProperties {
$user(): User | null;
$isGranted(area?: string, locale?: string | null): boolean;
$accounts(): Promise<void>;
$setToken(token: string | null): Promise<void>;
$removeToken(username?: string | null): Promise<void>;
}
}
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<Record<string, Account>> => {
const tokens = (window.localStorage.getItem('account-tokens') || fallback || '').split('|').filter((x) => !!x);
const accounts: Record<string, Account> = {};
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<string, Account>): void => {
store.setAccounts(accounts);
window.localStorage.setItem('account-tokens', Object.values(accounts).map((x) => x.token)
.join('|'));
};
const accounts = async (): Promise<void> => {
saveAccounts(await getAccounts(store.token));
};
const setToken = async (token: string | null): Promise<void> => {
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<void> => {
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,
},
};
});