@data-fair/lib-vue 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/session.d.ts +23 -0
- package/session.js +58 -17
package/package.json
CHANGED
package/session.d.ts
CHANGED
|
@@ -12,11 +12,30 @@ interface GenericCookies {
|
|
|
12
12
|
export interface SessionOptions {
|
|
13
13
|
sitePath: string;
|
|
14
14
|
directoryUrl: string;
|
|
15
|
+
defaultLang: string;
|
|
15
16
|
route?: RouteLocation;
|
|
16
17
|
logoutRedirectUrl?: string;
|
|
17
18
|
req?: IncomingMessage;
|
|
18
19
|
cookies?: GenericCookies;
|
|
19
20
|
customFetch?: typeof fetch;
|
|
21
|
+
siteInfo?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface Colors {
|
|
24
|
+
background: string;
|
|
25
|
+
surface: string;
|
|
26
|
+
primary: string;
|
|
27
|
+
secondary: string;
|
|
28
|
+
accent: string;
|
|
29
|
+
error: string;
|
|
30
|
+
info: string;
|
|
31
|
+
success: string;
|
|
32
|
+
warning: string;
|
|
33
|
+
admin: string;
|
|
34
|
+
}
|
|
35
|
+
export interface SiteInfo {
|
|
36
|
+
main?: boolean;
|
|
37
|
+
logo?: string;
|
|
38
|
+
colors: Colors;
|
|
20
39
|
}
|
|
21
40
|
export interface Session {
|
|
22
41
|
state: SessionState;
|
|
@@ -26,6 +45,7 @@ export interface Session {
|
|
|
26
45
|
accountRole: ComputedRef<SessionState['accountRole']>;
|
|
27
46
|
lang: ComputedRef<SessionState['lang']>;
|
|
28
47
|
dark: ComputedRef<SessionState['dark']>;
|
|
48
|
+
site: Ref<SiteInfo | null>;
|
|
29
49
|
loginUrl: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => string;
|
|
30
50
|
login: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => void;
|
|
31
51
|
logout: (redirect?: string) => Promise<void>;
|
|
@@ -34,6 +54,7 @@ export interface Session {
|
|
|
34
54
|
asAdmin: (user: any | null) => Promise<void>;
|
|
35
55
|
cancelDeletion: () => Promise<void>;
|
|
36
56
|
keepalive: () => Promise<void>;
|
|
57
|
+
refreshSiteInfo: () => Promise<void>;
|
|
37
58
|
switchDark: (value: boolean) => void;
|
|
38
59
|
switchLang: (value: string) => void;
|
|
39
60
|
topLocation: Ref<Location | undefined>;
|
|
@@ -56,6 +77,7 @@ export declare function createSession(initOptions: Partial<SessionOptions>): Pro
|
|
|
56
77
|
accountRole: ComputedRef<SessionState["accountRole"]>;
|
|
57
78
|
lang: ComputedRef<SessionState["lang"]>;
|
|
58
79
|
dark: ComputedRef<SessionState["dark"]>;
|
|
80
|
+
site: Ref<SiteInfo | null>;
|
|
59
81
|
loginUrl: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => string;
|
|
60
82
|
login: (redirect?: string, extraParams?: Record<string, string>, immediateRedirect?: true) => void;
|
|
61
83
|
logout: (redirect?: string) => Promise<void>;
|
|
@@ -64,6 +86,7 @@ export declare function createSession(initOptions: Partial<SessionOptions>): Pro
|
|
|
64
86
|
asAdmin: (user: any | null) => Promise<void>;
|
|
65
87
|
cancelDeletion: () => Promise<void>;
|
|
66
88
|
keepalive: () => Promise<void>;
|
|
89
|
+
refreshSiteInfo: () => Promise<void>;
|
|
67
90
|
switchDark: (value: boolean) => void;
|
|
68
91
|
switchLang: (value: string) => void;
|
|
69
92
|
topLocation: Ref<Location | undefined>;
|
package/session.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { reactive, computed, watch, inject } from 'vue'
|
|
1
|
+
import { reactive, computed, watch, inject, ref } from 'vue'
|
|
2
2
|
import { ofetch } from 'ofetch'
|
|
3
3
|
import { jwtDecode } from 'jwt-decode'
|
|
4
4
|
import cookiesModule from 'universal-cookie'
|
|
5
5
|
import Debug from 'debug'
|
|
6
|
+
import inIframe from '@data-fair/lib-utils/in-iframe.js'
|
|
6
7
|
const Cookies = cookiesModule
|
|
7
8
|
const debug = Debug('session')
|
|
8
9
|
debug.log = console.log.bind(console)
|
|
@@ -36,7 +37,7 @@ const goTo = (url) => {
|
|
|
36
37
|
}
|
|
37
38
|
if (url) { topLocation.href = url } else { topLocation.reload() }
|
|
38
39
|
}
|
|
39
|
-
const defaultOptions = { directoryUrl: '/simple-directory', sitePath: '' }
|
|
40
|
+
const defaultOptions = { directoryUrl: '/simple-directory', sitePath: '', defaultLang: 'fr' }
|
|
40
41
|
export async function getSession (initOptions) {
|
|
41
42
|
const options = { ...defaultOptions, ...initOptions }
|
|
42
43
|
const cookiesPath = options.sitePath + '/'
|
|
@@ -55,13 +56,14 @@ export async function getSession (initOptions) {
|
|
|
55
56
|
})
|
|
56
57
|
// the core state of the session that is filled by reading cookies
|
|
57
58
|
const state = reactive({})
|
|
59
|
+
const site = ref(null)
|
|
58
60
|
// cookies are the source of truth and this information is transformed into the state reactive object
|
|
59
61
|
const cookies = initOptions?.cookies ?? new Cookies(options.req?.headers.cookie)
|
|
60
|
-
const
|
|
62
|
+
const readState = () => {
|
|
61
63
|
const darkCookie = cookies.get('theme_dark')
|
|
62
64
|
state.dark = darkCookie === '1' || darkCookie === 'true'
|
|
63
65
|
const langCookie = cookies.get('i18n_lang')
|
|
64
|
-
|
|
66
|
+
state.lang = langCookie ?? options.defaultLang
|
|
65
67
|
const idToken = cookies.get('id_token')
|
|
66
68
|
const user = jwtDecodeAlive(idToken)
|
|
67
69
|
if (!user) {
|
|
@@ -107,16 +109,22 @@ export async function getSession (initOptions) {
|
|
|
107
109
|
}
|
|
108
110
|
state.accountRole = 'admin'
|
|
109
111
|
}
|
|
112
|
+
if (!ssr) {
|
|
113
|
+
const siteInfoStorage = getSiteInfoStorage()
|
|
114
|
+
site.value = siteInfoStorage ? siteInfoStorage.info : null
|
|
115
|
+
}
|
|
110
116
|
}
|
|
111
|
-
|
|
117
|
+
readState()
|
|
112
118
|
debug('initial state', state)
|
|
113
119
|
if (!ssr) {
|
|
114
120
|
// sessionData is also stored in localStorage as a way to access it in simpler pages that do not require use-session
|
|
115
121
|
// and in order to listen to storage event from other contexts and sync session info accross windows and tabs
|
|
116
|
-
|
|
117
|
-
|
|
122
|
+
if (!ssr) {
|
|
123
|
+
const storageListener = (event) => {
|
|
124
|
+
if (event.key === 'sd-session' + options.sitePath) { readState() }
|
|
125
|
+
}
|
|
126
|
+
window.addEventListener('storage', storageListener)
|
|
118
127
|
}
|
|
119
|
-
window.addEventListener('storage', storageListener)
|
|
120
128
|
// we cannot use onUnmounted here or we get warnings "onUnmounted is called when there is no active component instance to be associated with. "
|
|
121
129
|
// TODO: should we have another cleanup mechanism ?
|
|
122
130
|
// onUnmounted(() => { window.removeEventListener('storage', storageListener) })
|
|
@@ -166,7 +174,7 @@ export async function getSession (initOptions) {
|
|
|
166
174
|
const switchOrganization = (org, dep) => {
|
|
167
175
|
if (org) { cookies.set('id_token_org', org, { path: cookiesPath }) } else { cookies.remove('id_token_org') }
|
|
168
176
|
if (dep) { cookies.set('id_token_dep', dep, { path: cookiesPath }) } else { cookies.remove('id_token_dep') }
|
|
169
|
-
|
|
177
|
+
readState()
|
|
170
178
|
}
|
|
171
179
|
const setAdminMode = async (adminMode, redirect) => {
|
|
172
180
|
if (adminMode) {
|
|
@@ -185,36 +193,67 @@ export async function getSession (initOptions) {
|
|
|
185
193
|
} else {
|
|
186
194
|
await customFetch(`${options.directoryUrl}/api/auth/asadmin`, { method: 'DELETE' })
|
|
187
195
|
}
|
|
188
|
-
|
|
196
|
+
readState()
|
|
189
197
|
}
|
|
190
198
|
const cancelDeletion = async () => {
|
|
191
199
|
if (state.user == null) { return }
|
|
192
200
|
await customFetch(`${options.directoryUrl}/api/users/${state.user.id}`, { method: 'PATCH', body: ({ plannedDeletion: null }) })
|
|
193
|
-
|
|
201
|
+
readState()
|
|
194
202
|
}
|
|
195
203
|
const switchDark = (value) => {
|
|
196
204
|
const maxAge = 60 * 60 * 24 * 365 // 1 year
|
|
197
205
|
cookies.set('theme_dark', `${value}`, { maxAge, path: cookiesPath })
|
|
198
|
-
|
|
206
|
+
readState()
|
|
199
207
|
}
|
|
200
208
|
const switchLang = (value) => {
|
|
201
209
|
const maxAge = 60 * 60 * 24 * 365 // 1 year
|
|
202
210
|
cookies.set('i18n_lang', value, { maxAge, path: cookiesPath })
|
|
203
|
-
|
|
211
|
+
readState()
|
|
204
212
|
}
|
|
205
213
|
const keepalive = async () => {
|
|
206
214
|
if (state.user == null) { return }
|
|
207
|
-
|
|
215
|
+
if (!ssr) {
|
|
216
|
+
window.localStorage.setItem('sd-keepalive' + options.sitePath, `${new Date().getTime()}`)
|
|
217
|
+
}
|
|
208
218
|
await customFetch(`${options.directoryUrl}/api/auth/keepalive`, { method: 'POST' })
|
|
209
|
-
|
|
219
|
+
readState()
|
|
220
|
+
}
|
|
221
|
+
const getSiteInfoStorage = () => {
|
|
222
|
+
const siteInfoStorageStr = window.localStorage.getItem('sd-site-info' + options.sitePath)
|
|
223
|
+
return siteInfoStorageStr ? JSON.parse(siteInfoStorageStr) : null
|
|
224
|
+
}
|
|
225
|
+
const setSiteInfoStorage = (siteInfo) => {
|
|
226
|
+
const siteInfoStorage = { info: siteInfo, updatedAt: new Date().getTime() }
|
|
227
|
+
window.localStorage.setItem('sd-site-info' + options.sitePath, JSON.stringify(siteInfoStorage))
|
|
228
|
+
}
|
|
229
|
+
const refreshSiteInfo = async () => {
|
|
230
|
+
const siteInfo = await customFetch(`${options.directoryUrl}/api/sites/_public`) ?? null
|
|
231
|
+
site.value = siteInfo
|
|
232
|
+
if (!ssr) {
|
|
233
|
+
setSiteInfoStorage(siteInfo)
|
|
234
|
+
}
|
|
210
235
|
}
|
|
211
236
|
// immediately performs a keepalive, but only on top windows (not iframes or popups)
|
|
212
237
|
// and only if it was not done very recently (maybe from a refreshed page next to this one)
|
|
213
|
-
|
|
238
|
+
// also run an auto-refresh loop
|
|
239
|
+
if (!ssr && !inIframe) {
|
|
214
240
|
const lastKeepalive = window.localStorage.getItem('sd-keepalive' + options.sitePath)
|
|
215
|
-
if (!lastKeepalive || (new Date().getTime() - Number(lastKeepalive)) > 10000) {
|
|
241
|
+
if (state.user && (!lastKeepalive || (new Date().getTime() - Number(lastKeepalive)) > 10000)) {
|
|
216
242
|
await keepalive()
|
|
217
243
|
}
|
|
244
|
+
if (options.siteInfo) {
|
|
245
|
+
const lastSiteInfoStorage = getSiteInfoStorage()
|
|
246
|
+
if (!lastSiteInfoStorage || (new Date().getTime() - Number(lastSiteInfoStorage.updatedAt)) > 10000) {
|
|
247
|
+
await refreshSiteInfo()
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const refreshLoopDelay = 10 * 60 * 1000 // 10 minutes
|
|
251
|
+
setInterval(() => {
|
|
252
|
+
const lastKeepalive = window.localStorage.getItem('sd-keepalive' + options.sitePath)
|
|
253
|
+
if (!lastKeepalive || (new Date().getTime() - Number(lastKeepalive)) > refreshLoopDelay / 2) {
|
|
254
|
+
keepalive().catch(err => console.error(err))
|
|
255
|
+
}
|
|
256
|
+
}, refreshLoopDelay)
|
|
218
257
|
}
|
|
219
258
|
const session = {
|
|
220
259
|
state,
|
|
@@ -224,6 +263,7 @@ export async function getSession (initOptions) {
|
|
|
224
263
|
accountRole: computed(() => state.accountRole),
|
|
225
264
|
dark: computed(() => state.dark),
|
|
226
265
|
lang: computed(() => state.lang),
|
|
266
|
+
site,
|
|
227
267
|
loginUrl,
|
|
228
268
|
login,
|
|
229
269
|
logout,
|
|
@@ -232,6 +272,7 @@ export async function getSession (initOptions) {
|
|
|
232
272
|
asAdmin,
|
|
233
273
|
cancelDeletion,
|
|
234
274
|
keepalive,
|
|
275
|
+
refreshSiteInfo,
|
|
235
276
|
switchDark,
|
|
236
277
|
switchLang,
|
|
237
278
|
topLocation,
|