@inwink-mcp/backoffice-extension-helpers 1.0.1 → 1.0.2
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/dist/cli.js
CHANGED
|
@@ -252,7 +252,8 @@ function bootstrap(opts) {
|
|
|
252
252
|
error(`La cible existe et n'est pas un dossier : ${destDir}`);
|
|
253
253
|
return 1;
|
|
254
254
|
}
|
|
255
|
-
const
|
|
255
|
+
const IGNORED_DIRS = /* @__PURE__ */ new Set([".claude", ".github", ".git", ".vscode", ".idea", ".cursor"]);
|
|
256
|
+
const entries = fs3.readdirSync(destDir).filter((e) => !IGNORED_DIRS.has(e));
|
|
256
257
|
if (entries.length > 0 && !opts.force) {
|
|
257
258
|
error(`Le dossier cible n'est pas vide : ${destDir}`);
|
|
258
259
|
info("Utilisez --force pour \xE9crire malgr\xE9 tout, ou --dir vers un dossier vide.");
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/auth/AuthProvider.tsx
|
|
2
|
-
import { createContext, useEffect, useState, type PropsWithChildren } from 'react';
|
|
2
|
+
import { createContext, useCallback, useEffect, useMemo, useRef, useState, type PropsWithChildren } from 'react';
|
|
3
3
|
import type { User } from 'oidc-client-ts';
|
|
4
4
|
import { userManager } from './config';
|
|
5
5
|
|
|
@@ -24,6 +24,8 @@ function isOidcCallback(): boolean {
|
|
|
24
24
|
export function AuthProvider({ children }: PropsWithChildren) {
|
|
25
25
|
const [user, setUser] = useState<User | null>(null);
|
|
26
26
|
const [isLoading, setIsLoading] = useState(true);
|
|
27
|
+
const userRef = useRef(user);
|
|
28
|
+
useEffect(() => { userRef.current = user; }, [user]);
|
|
27
29
|
|
|
28
30
|
useEffect(() => {
|
|
29
31
|
let isMounted = true;
|
|
@@ -56,24 +58,40 @@ export function AuthProvider({ children }: PropsWithChildren) {
|
|
|
56
58
|
};
|
|
57
59
|
}, []);
|
|
58
60
|
|
|
61
|
+
const login = useCallback(async () => {
|
|
62
|
+
setIsLoading(true);
|
|
63
|
+
await userManager.signinRedirect();
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const logout = useCallback(async () => {
|
|
67
|
+
await userManager.removeUser();
|
|
68
|
+
setUser(null);
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
const getAccessToken = useCallback(() => userRef.current?.access_token ?? null, []);
|
|
72
|
+
|
|
73
|
+
const renewToken = useCallback(async () => {
|
|
74
|
+
try {
|
|
75
|
+
const renewed = await userManager.signinSilent();
|
|
76
|
+
setUser(renewed);
|
|
77
|
+
return renewed?.access_token ?? null;
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
const value = useMemo(() => ({
|
|
84
|
+
isAuthenticated: Boolean(user && !user.expired),
|
|
85
|
+
isLoading,
|
|
86
|
+
login,
|
|
87
|
+
logout,
|
|
88
|
+
user,
|
|
89
|
+
getAccessToken,
|
|
90
|
+
renewToken,
|
|
91
|
+
}), [user, isLoading]);
|
|
92
|
+
|
|
59
93
|
return (
|
|
60
|
-
<AuthContext.Provider value={
|
|
61
|
-
isAuthenticated: Boolean(user && !user.expired),
|
|
62
|
-
isLoading,
|
|
63
|
-
login: async () => { setIsLoading(true); await userManager.signinRedirect(); },
|
|
64
|
-
logout: async () => { await userManager.removeUser(); setUser(null); },
|
|
65
|
-
user,
|
|
66
|
-
getAccessToken: () => user?.access_token ?? null,
|
|
67
|
-
renewToken: async () => {
|
|
68
|
-
try {
|
|
69
|
-
const renewed = await userManager.signinSilent();
|
|
70
|
-
setUser(renewed);
|
|
71
|
-
return renewed?.access_token ?? null;
|
|
72
|
-
} catch {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
}}>
|
|
94
|
+
<AuthContext.Provider value={value}>
|
|
77
95
|
{children}
|
|
78
96
|
</AuthContext.Provider>
|
|
79
97
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
1
2
|
import { useAuth } from './useAuth';
|
|
2
3
|
|
|
3
4
|
/** Erreur levée lorsqu'une réponse API n'est pas OK (hors 401 géré par renouvellement). */
|
|
@@ -38,64 +39,66 @@ async function safeBody(response: Response): Promise<unknown> {
|
|
|
38
39
|
export function useInwinkApi(baseUrl: string): InwinkApi {
|
|
39
40
|
const { getAccessToken, renewToken, login } = useAuth();
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
async function request<T = unknown>(path: string, init?: RequestInit): Promise<T> {
|
|
53
|
-
let token = getAccessToken();
|
|
54
|
-
if (!token) {
|
|
55
|
-
await login();
|
|
56
|
-
throw new InwinkApiError(401, 'Non authentifié.');
|
|
42
|
+
return useMemo(() => {
|
|
43
|
+
async function doFetch(path: string, token: string, init?: RequestInit): Promise<Response> {
|
|
44
|
+
return fetch(`${baseUrl}${path}`, {
|
|
45
|
+
...init,
|
|
46
|
+
headers: {
|
|
47
|
+
Authorization: `Bearer ${token}`,
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
...(init?.headers as Record<string, string> | undefined),
|
|
50
|
+
},
|
|
51
|
+
});
|
|
57
52
|
}
|
|
58
53
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
token = renewed;
|
|
65
|
-
response = await doFetch(path, token, init);
|
|
54
|
+
async function request<T = unknown>(path: string, init?: RequestInit): Promise<T> {
|
|
55
|
+
let token = getAccessToken();
|
|
56
|
+
if (!token) {
|
|
57
|
+
await login();
|
|
58
|
+
throw new InwinkApiError(401, 'Non authentifié.');
|
|
66
59
|
}
|
|
60
|
+
|
|
61
|
+
let response = await doFetch(path, token, init);
|
|
62
|
+
|
|
67
63
|
if (response.status === 401) {
|
|
68
|
-
await
|
|
69
|
-
|
|
64
|
+
const renewed = await renewToken();
|
|
65
|
+
if (renewed) {
|
|
66
|
+
token = renewed;
|
|
67
|
+
response = await doFetch(path, token, init);
|
|
68
|
+
}
|
|
69
|
+
if (response.status === 401) {
|
|
70
|
+
await login();
|
|
71
|
+
throw new InwinkApiError(401, 'Session expirée, reconnexion requise.');
|
|
72
|
+
}
|
|
70
73
|
}
|
|
71
|
-
}
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new InwinkApiError(response.status, await safeBody(response));
|
|
77
|
+
}
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
if (response.status === 204) {
|
|
80
|
+
return undefined as T;
|
|
81
|
+
}
|
|
82
|
+
return (await response.json()) as T;
|
|
79
83
|
}
|
|
80
|
-
return (await response.json()) as T;
|
|
81
|
-
}
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
const withBody =
|
|
86
|
+
(method: string) =>
|
|
87
|
+
<T = unknown>(path: string, body?: unknown, init?: RequestInit): Promise<T> =>
|
|
88
|
+
request<T>(path, {
|
|
89
|
+
...init,
|
|
90
|
+
method,
|
|
91
|
+
body: body === undefined ? init?.body : JSON.stringify(body),
|
|
92
|
+
});
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
return {
|
|
95
|
+
request,
|
|
96
|
+
get: <T = unknown>(path: string, init?: RequestInit) =>
|
|
97
|
+
request<T>(path, { ...init, method: 'GET' }),
|
|
98
|
+
post: withBody('POST'),
|
|
99
|
+
put: withBody('PUT'),
|
|
100
|
+
del: <T = unknown>(path: string, init?: RequestInit) =>
|
|
101
|
+
request<T>(path, { ...init, method: 'DELETE' }),
|
|
102
|
+
};
|
|
103
|
+
}, [baseUrl, getAccessToken, renewToken, login]);
|
|
101
104
|
}
|