codevdesign 1.0.8 → 1.0.10
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/composants/csqcChaise/chaiseConteneur.vue +363 -363
- package/composants/csqcRechercheUtilisateur.vue +197 -197
- package/index.ts +73 -74
- package/outils/rafraichisseurToken.ts +29 -40
- package/package.json +1 -1
- package/outils/appAxios.ts +0 -116
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import { useAppStore } from '@/store/appStore'
|
|
2
1
|
|
|
3
2
|
class RafraichisseurToken {
|
|
4
3
|
private intervalleEnSecondes = 15
|
|
5
4
|
private secondesAvantExpirationTokenPourRafraichir = 20
|
|
6
5
|
private skewSeconds = 5 // marge anti-derives d’horloge
|
|
7
6
|
private popupAffiche = false
|
|
8
|
-
private appStore: ReturnType<typeof useAppStore> | null = null
|
|
9
7
|
private timerId: number | null = null
|
|
10
8
|
|
|
11
9
|
// Lance une seule fois
|
|
12
|
-
public async demarrer(): Promise<void> {
|
|
13
|
-
|
|
14
|
-
await this.verifierToken()
|
|
10
|
+
public async demarrer(nomCookie: string | null, urlPortail: string): Promise<void> {
|
|
11
|
+
if (nomCookie == null || nomCookie === '') nomCookie = 'csqc_jeton_secure_expiration'
|
|
12
|
+
await this.verifierToken(nomCookie, urlPortail)
|
|
15
13
|
if (this.timerId != null) return
|
|
16
14
|
this.timerId = window.setInterval(() => {
|
|
17
|
-
this.verifierToken()
|
|
15
|
+
this.verifierToken(nomCookie, urlPortail)
|
|
18
16
|
}, this.intervalleEnSecondes * 1000)
|
|
19
17
|
}
|
|
20
18
|
|
|
@@ -26,13 +24,12 @@ class RafraichisseurToken {
|
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
private async verifierToken(): Promise<void> {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const tokenEncode = this.lireCookie(modele.nomCookie)
|
|
27
|
+
private async verifierToken(nomCookie: string, urlPortail: string): Promise<void> {
|
|
28
|
+
|
|
29
|
+
if (this.popupAffiche || !nomCookie) return
|
|
30
|
+
const tokenEncode = this.lireCookie(nomCookie)
|
|
34
31
|
if (!tokenEncode) {
|
|
35
|
-
await this.rafraichir()
|
|
32
|
+
await this.rafraichir(nomCookie, urlPortail)
|
|
36
33
|
return
|
|
37
34
|
}
|
|
38
35
|
|
|
@@ -40,29 +37,27 @@ class RafraichisseurToken {
|
|
|
40
37
|
try {
|
|
41
38
|
token = this.parseJwt(tokenEncode)
|
|
42
39
|
} catch {
|
|
43
|
-
await this.rafraichir()
|
|
40
|
+
await this.rafraichir(nomCookie, urlPortail)
|
|
44
41
|
return
|
|
45
42
|
}
|
|
46
43
|
|
|
47
44
|
const exp = Number(token?.exp)
|
|
48
45
|
if (!Number.isFinite(exp)) {
|
|
49
46
|
// exp manquant/invalide → tente refresh
|
|
50
|
-
await this.rafraichir()
|
|
47
|
+
await this.rafraichir(nomCookie, urlPortail)
|
|
51
48
|
return
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
const now = Math.floor(Date.now() / 1000)
|
|
55
52
|
const refreshAt = exp - this.secondesAvantExpirationTokenPourRafraichir - this.skewSeconds
|
|
56
53
|
if (now >= refreshAt) {
|
|
57
|
-
await this.rafraichir()
|
|
54
|
+
await this.rafraichir(nomCookie, urlPortail)
|
|
58
55
|
}
|
|
59
56
|
}
|
|
60
57
|
|
|
61
|
-
private async rafraichir(): Promise<void> {
|
|
62
|
-
if (!
|
|
63
|
-
const
|
|
64
|
-
if (!modele) return
|
|
65
|
-
const url = this.getRefreshUrl()
|
|
58
|
+
private async rafraichir(nomCookie: string, urlPortail: string): Promise<void> {
|
|
59
|
+
if (!nomCookie) return
|
|
60
|
+
const url = this.getRefreshUrl(urlPortail)
|
|
66
61
|
const controller = new AbortController()
|
|
67
62
|
const timeout = setTimeout(() => controller.abort(), 10_000)
|
|
68
63
|
|
|
@@ -79,20 +74,20 @@ class RafraichisseurToken {
|
|
|
79
74
|
// redirection (souvent => login) → traiter comme non auth
|
|
80
75
|
|
|
81
76
|
if (resp.type === 'opaqueredirect' || resp.status === 302) {
|
|
82
|
-
this.rafraichirParIframe()
|
|
77
|
+
this.rafraichirParIframe(nomCookie, urlPortail)
|
|
83
78
|
return
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
// OK ou No Content: le cookie devrait être réécrit
|
|
87
82
|
if (resp.status === 200 || resp.status === 204) {
|
|
88
|
-
const jeton = this.lireCookie(
|
|
89
|
-
if (!jeton) this.rafraichirParIframe()
|
|
83
|
+
const jeton = this.lireCookie(nomCookie)
|
|
84
|
+
if (!jeton) this.rafraichirParIframe(nomCookie, urlPortail)
|
|
90
85
|
return
|
|
91
86
|
}
|
|
92
87
|
|
|
93
88
|
// non auth / expiré (401, 419) + IIS timeout (440)
|
|
94
89
|
if (resp.status === 401 || resp.status === 419 || resp.status === 440) {
|
|
95
|
-
this.rafraichirParIframe()
|
|
90
|
+
this.rafraichirParIframe(nomCookie, urlPortail)
|
|
96
91
|
return
|
|
97
92
|
}
|
|
98
93
|
|
|
@@ -106,41 +101,35 @@ class RafraichisseurToken {
|
|
|
106
101
|
}
|
|
107
102
|
}
|
|
108
103
|
|
|
109
|
-
private rafraichirParIframe() {
|
|
110
|
-
if (!this.appStore) this.appStore = useAppStore()
|
|
104
|
+
private rafraichirParIframe(nomCookie: string, urlPortail: string): void {
|
|
111
105
|
// Pour éviter les cross référence, on créé un iframe qui appel portail et force la MAJ du jeton ou l'invalidation du jeton, si jamais l'utilisateur n'est plus connecté
|
|
112
106
|
// ajax vers le refresh
|
|
113
107
|
let iframe = document.createElement('iframe')
|
|
114
|
-
const url = this.getRefreshUrl()
|
|
108
|
+
const url = this.getRefreshUrl(urlPortail)
|
|
115
109
|
iframe.src = `${url}?urlRetour=${encodeURI(window.localStorage.href)}`
|
|
116
110
|
iframe.id = 'idRafrToken'
|
|
117
111
|
iframe.style.display = 'none'
|
|
118
112
|
document.body.appendChild(iframe)
|
|
119
113
|
iframe.onload = () => {
|
|
120
|
-
const jetonCSQC = this.lireCookie(
|
|
114
|
+
const jetonCSQC = this.lireCookie(nomCookie)
|
|
121
115
|
if (jetonCSQC == null || jetonCSQC === '') {
|
|
122
|
-
this.estDeconnecteAzure()
|
|
116
|
+
this.estDeconnecteAzure(urlPortail)
|
|
123
117
|
}
|
|
124
118
|
|
|
125
119
|
iframe.remove()
|
|
126
120
|
}
|
|
127
121
|
}
|
|
128
122
|
|
|
129
|
-
private estDeconnecteAzure(): void {
|
|
123
|
+
private estDeconnecteAzure(urlPortail: string): void {
|
|
130
124
|
//on envoie au portail, pas le choix
|
|
131
|
-
if (!this.appStore) this.appStore = useAppStore()
|
|
132
125
|
|
|
133
126
|
const retour = encodeURI(window.location.href)
|
|
134
|
-
window.open(`${
|
|
127
|
+
window.open(`${urlPortail}/home/SeConnecter?urlRetour=${retour}`, '_self')
|
|
135
128
|
return
|
|
136
129
|
}
|
|
137
130
|
|
|
138
|
-
public deconnecterPortail(): void {
|
|
139
|
-
|
|
140
|
-
const modele = this.appStore?.appModele
|
|
141
|
-
if (!modele) return
|
|
142
|
-
|
|
143
|
-
window.location.replace(`${modele.urlPortail}deconnecte?urlRetour=${encodeURIComponent(window.location.href)}`)
|
|
131
|
+
public deconnecterPortail(urlPortail: string): void {
|
|
132
|
+
window.location.replace(`${urlPortail}deconnecte?urlRetour=${encodeURIComponent(window.location.href)}`)
|
|
144
133
|
}
|
|
145
134
|
|
|
146
135
|
|
|
@@ -175,9 +164,9 @@ class RafraichisseurToken {
|
|
|
175
164
|
}
|
|
176
165
|
|
|
177
166
|
// URL refresh selon env (dev = proxy Vite ; prod = portail)
|
|
178
|
-
private getRefreshUrl(): string {
|
|
167
|
+
private getRefreshUrl(urlPortail: string): string {
|
|
179
168
|
if (import.meta.env.MODE === 'development') return '/portail-refresh'
|
|
180
|
-
return
|
|
169
|
+
return urlPortail + '/Home/Refresh'
|
|
181
170
|
}
|
|
182
171
|
}
|
|
183
172
|
|
package/package.json
CHANGED
package/outils/appAxios.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import axios, { type AxiosInstance, type AxiosError, type AxiosResponse } from 'axios'
|
|
2
|
-
import { useAppStore } from '@/store/appStore'
|
|
3
|
-
import router from '@/router'
|
|
4
|
-
|
|
5
|
-
type ApiReponse<T = unknown> =
|
|
6
|
-
// Succès
|
|
7
|
-
| {
|
|
8
|
-
succes: true
|
|
9
|
-
resultat: T
|
|
10
|
-
status?: number
|
|
11
|
-
message?: string
|
|
12
|
-
location?: string
|
|
13
|
-
parametres?: unknown
|
|
14
|
-
[k: string]: unknown
|
|
15
|
-
}
|
|
16
|
-
// Échec (le backend peut quand même renvoyer resultat null/absent)
|
|
17
|
-
| {
|
|
18
|
-
succes: false
|
|
19
|
-
resultat?: unknown
|
|
20
|
-
status?: number
|
|
21
|
-
message?: string
|
|
22
|
-
location?: string
|
|
23
|
-
parametres?: unknown
|
|
24
|
-
[k: string]: unknown
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let client: AxiosInstance | null = null
|
|
28
|
-
let cachedBaseUrl = '' // pour éviter de régénérer une instance axios si rien n’a changé
|
|
29
|
-
|
|
30
|
-
export default {
|
|
31
|
-
clearCache: false,
|
|
32
|
-
|
|
33
|
-
getAxios(): AxiosInstance {
|
|
34
|
-
// Singleton + clearCache
|
|
35
|
-
if (client && !this.clearCache) return client
|
|
36
|
-
|
|
37
|
-
const appStore = useAppStore()
|
|
38
|
-
|
|
39
|
-
const rawUrl = appStore.modeleCharger
|
|
40
|
-
? appStore.appModele!.urlBase
|
|
41
|
-
: window.location.origin + import.meta.env.BASE_URL
|
|
42
|
-
|
|
43
|
-
const urlBase = rawUrl.endsWith('/') ? rawUrl.slice(0, -1) : rawUrl
|
|
44
|
-
|
|
45
|
-
// Si la base URL n'a pas changé et qu'on a déjà un client, on le renvoie
|
|
46
|
-
if (client && cachedBaseUrl === urlBase && !this.clearCache) return client
|
|
47
|
-
cachedBaseUrl = urlBase
|
|
48
|
-
|
|
49
|
-
client = axios.create({
|
|
50
|
-
baseURL: `${urlBase}/api`,
|
|
51
|
-
withCredentials: true,
|
|
52
|
-
timeout: 30_000,
|
|
53
|
-
headers: {
|
|
54
|
-
Accept: 'application/json',
|
|
55
|
-
'Content-Type': 'application/json',
|
|
56
|
-
'X-Requested-With': 'XMLHttpRequest',
|
|
57
|
-
},
|
|
58
|
-
// validateStatus: (s) => s >= 200 && s < 300, // défaut axios
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
client.interceptors.response.use(
|
|
62
|
-
(response: AxiosResponse<any>) => {
|
|
63
|
-
const data = response.data
|
|
64
|
-
|
|
65
|
-
// Détection de la réponse { succes, resultat }
|
|
66
|
-
if (data && typeof data === 'object' && 'succes' in data) {
|
|
67
|
-
const env = data as ApiReponse
|
|
68
|
-
return env.succes === true ? env.resultat : Promise.reject(env)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Sinon, renvoyer data si présent, sinon la réponse complète
|
|
72
|
-
return data ?? response
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
(error: AxiosError<any>) => {
|
|
76
|
-
const status = error.response?.status
|
|
77
|
-
const payload = error.response?.data?.resultat ?? { message: error.message }
|
|
78
|
-
|
|
79
|
-
// 403 / 404
|
|
80
|
-
if (status === 403 || status === 404) {
|
|
81
|
-
try {
|
|
82
|
-
appStore.lancerErreur(payload)
|
|
83
|
-
if (router.currentRoute.value.name !== '403') {
|
|
84
|
-
router.push({ name: '403' })
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
// no-op
|
|
88
|
-
}
|
|
89
|
-
return Promise.reject(payload)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// gérer les autres 4XX ici
|
|
93
|
-
|
|
94
|
-
// Log minimal
|
|
95
|
-
console.error('HTTP error', {
|
|
96
|
-
status,
|
|
97
|
-
url: error.config?.url,
|
|
98
|
-
payload,
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
// Remonter l’erreur normalisée dans appstore
|
|
102
|
-
try {
|
|
103
|
-
if (payload?.resultat) appStore.lancerErreur(payload)
|
|
104
|
-
else if (payload) appStore.lancerErreur(payload)
|
|
105
|
-
} catch {
|
|
106
|
-
// no-op
|
|
107
|
-
}
|
|
108
|
-
return Promise.reject(payload)
|
|
109
|
-
},
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
// reset le flag si on l’avait utilisé pour forcer la recréation de l'instance
|
|
113
|
-
this.clearCache = false
|
|
114
|
-
return client
|
|
115
|
-
},
|
|
116
|
-
}
|