import type { Pinia } from 'pinia';
|
|
import type { App } from 'vue';
|
|
import { createPinia } from 'pinia';
|
import SecureLS from 'secure-ls';
|
|
import { createSessionAuthStorage } from './session-auth';
|
|
let pinia: Pinia;
|
let sessionLogin: (<T>(action: (token: null | string) => Promise<T>) => Promise<T>) | undefined;
|
|
/** Serialize password login across tabs when the browser supports Web Locks. */
|
export async function withSessionAuthLogin<T>(action: (token: null | string) => Promise<T>): Promise<T> {
|
return sessionLogin ? sessionLogin(action) : action(null);
|
}
|
|
export interface InitStoreOptions {
|
// 子应用是否共用token
|
isCommonToken?: boolean;
|
/**
|
* @zh_CN 应用名,由于 @vben/stores 是公用的,后续可能有多个app,为了防止多个app缓存冲突,可在这里配置应用名,应用名将被用于持久化的前缀
|
*/
|
namespace: string;
|
/** Keep authentication in the page session and share it with live tabs. */
|
sessionAuth?: boolean;
|
}
|
|
/**
|
* @zh_CN 初始化pinia
|
*/
|
export async function initStores(app: App, options: InitStoreOptions) {
|
const { createPersistedState } = await import('pinia-plugin-persistedstate');
|
pinia = createPinia();
|
let { namespace } = options;
|
const ls = new SecureLS({
|
encodingType: 'aes',
|
encryptionSecret: import.meta.env.VITE_APP_STORE_SECURE_KEY,
|
isCompression: true,
|
// @ts-ignore secure-ls does not have a type definition for this
|
metaKey: `${namespace}-secure-meta`,
|
});
|
const authNamespace = options.isCommonToken ? namespace.split('_')[0]! : namespace;
|
const authKey = `${authNamespace}-core-access`;
|
let sessionAuth: ReturnType<typeof createSessionAuthStorage> | undefined;
|
sessionLogin = undefined;
|
if (options.sessionAuth) {
|
const sessionLs = import.meta.env.DEV
|
? undefined
|
: new SecureLS({
|
encodingType: 'aes',
|
encryptionSecret: import.meta.env.VITE_APP_STORE_SECURE_KEY,
|
isCompression: true,
|
// @ts-ignore secure-ls supports storage and metaKey at runtime.
|
storage: sessionStorage,
|
metaKey: `${authNamespace}-session-secure-meta`,
|
});
|
sessionAuth = createSessionAuthStorage({
|
key: authKey,
|
session: sessionLs
|
? {
|
getItem: (key) => sessionLs.get(key),
|
setItem: (key, value) => sessionLs.set(key, value),
|
}
|
: sessionStorage,
|
persistent: localStorage,
|
channel: typeof BroadcastChannel === 'undefined' ? undefined : new BroadcastChannel(`${authKey}-session`),
|
events: window,
|
// Rebuild user information, routes and permissions for the new identity.
|
onRemoteChange: () => window.location.reload(),
|
});
|
await sessionAuth.start();
|
const authentication = sessionAuth;
|
sessionLogin = async (action) => {
|
const run = async () => {
|
await authentication.start();
|
return action(authentication.getAccessToken());
|
};
|
return navigator.locks ? navigator.locks.request(`${authKey}-login`, run) : run();
|
};
|
app.onUnmount(() => sessionAuth?.dispose());
|
import.meta.hot?.dispose(() => sessionAuth?.dispose());
|
}
|
pinia.use(
|
createPersistedState({
|
// key $appName-$store.id
|
key: (storeKey) => {
|
if (options.isCommonToken && storeKey == 'core-access' && namespace.indexOf('_') > 0) {
|
namespace = namespace.slice(0, namespace.indexOf('_'));
|
}
|
return `${namespace}-${storeKey}`;
|
},
|
storage: {
|
getItem(key) {
|
if (sessionAuth && key === authKey) return sessionAuth.getItem();
|
return import.meta.env.DEV ? localStorage.getItem(key) : ls.get(key);
|
},
|
setItem(key, value) {
|
if (sessionAuth && key === authKey) {
|
sessionAuth.setItem(key, value);
|
} else if (import.meta.env.DEV) {
|
localStorage.setItem(key, value);
|
} else {
|
ls.set(key, value);
|
}
|
},
|
},
|
}),
|
);
|
app.use(pinia);
|
return pinia;
|
}
|
|
export function resetAllStores() {
|
if (!pinia) {
|
console.error('Pinia is not installed');
|
return;
|
}
|
const allStores = (pinia as any)._s;
|
for (const [_key, store] of allStores) {
|
if (!_key.includes('jnpfUniver')) store?.$reset();
|
}
|
}
|
|
export function getAllStores() {
|
if (!pinia) {
|
console.error('Pinia is not installed');
|
return [];
|
}
|
const allStores = (pinia as any)._s;
|
return allStores;
|
}
|