@rebasepro/auth 0.2.3 → 0.2.5
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/api.d.ts +5 -1
- package/dist/hooks/useBackendUserManagement.d.ts +7 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +96 -140
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +96 -140
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +11 -3
- package/package.json +30 -4
- package/src/api.ts +5 -1
- package/src/hooks/useBackendUserManagement.ts +113 -178
- package/src/hooks/useRebaseAuthController.ts +16 -13
- package/src/index.ts +1 -1
- package/src/types.ts +9 -3
package/dist/api.d.ts
CHANGED
|
@@ -132,7 +132,11 @@ export interface AuthConfigResponse {
|
|
|
132
132
|
/** Whether new user registration is enabled */
|
|
133
133
|
registrationEnabled: boolean;
|
|
134
134
|
/** Whether email service is configured */
|
|
135
|
-
emailServiceEnabled
|
|
135
|
+
emailServiceEnabled?: boolean;
|
|
136
|
+
/** Whether password reset is supported */
|
|
137
|
+
passwordReset?: boolean;
|
|
138
|
+
/** Whether email verification is supported */
|
|
139
|
+
emailVerification?: boolean;
|
|
136
140
|
/** Complete list of enabled OAuth provider IDs (e.g. ["google", "github", "discord"]) */
|
|
137
141
|
enabledProviders: string[];
|
|
138
142
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { User } from "@rebasepro/types";
|
|
2
2
|
/**
|
|
3
3
|
* UserManagement interface - compatible with @rebasepro/user_management
|
|
4
4
|
* Defined inline to avoid dependency on that package
|
|
5
5
|
*/
|
|
6
6
|
export interface UserManagement<USER extends User = User> {
|
|
7
7
|
loading: boolean;
|
|
8
|
+
hasAdminUsers?: boolean;
|
|
8
9
|
users: USER[];
|
|
9
10
|
saveUser: (user: USER) => Promise<USER>;
|
|
10
11
|
createUser?: (user: USER) => Promise<{
|
|
@@ -18,13 +19,9 @@ export interface UserManagement<USER extends User = User> {
|
|
|
18
19
|
temporaryPassword?: string;
|
|
19
20
|
}>;
|
|
20
21
|
deleteUser: (user: USER) => Promise<void>;
|
|
21
|
-
roles: Role[];
|
|
22
|
-
saveRole: (role: Role) => Promise<void>;
|
|
23
|
-
deleteRole: (role: Role) => Promise<void>;
|
|
24
22
|
isAdmin?: boolean;
|
|
25
23
|
allowDefaultRolesCreation?: boolean;
|
|
26
|
-
|
|
27
|
-
defineRolesFor: (user: User) => Promise<Role[] | undefined> | Role[] | undefined;
|
|
24
|
+
defineRolesFor: (user: User) => Promise<string[] | undefined> | string[] | undefined;
|
|
28
25
|
getUser: (uid: string) => User | null;
|
|
29
26
|
/**
|
|
30
27
|
* Search users with server-side pagination.
|
|
@@ -43,14 +40,16 @@ export interface UserManagement<USER extends User = User> {
|
|
|
43
40
|
total: number;
|
|
44
41
|
}>;
|
|
45
42
|
usersError?: Error;
|
|
46
|
-
rolesError?: Error;
|
|
47
43
|
bootstrapAdmin?: () => Promise<void>;
|
|
48
44
|
}
|
|
49
45
|
export interface BackendUserManagementConfig {
|
|
50
46
|
/**
|
|
51
47
|
* The Rebase Client instance
|
|
52
48
|
*/
|
|
53
|
-
client?:
|
|
49
|
+
client?: {
|
|
50
|
+
baseUrl?: string;
|
|
51
|
+
resolveToken?: () => Promise<string | null>;
|
|
52
|
+
};
|
|
54
53
|
/**
|
|
55
54
|
* Base API URL for the backend (optional, extracted from client if not provided)
|
|
56
55
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export type { RebaseAuthController, RebaseAuthControllerProps, AuthTokens, UserI
|
|
|
10
10
|
export { useRebaseAuthController } from "./hooks/useRebaseAuthController";
|
|
11
11
|
export { useBackendUserManagement } from "./hooks/useBackendUserManagement";
|
|
12
12
|
export type { BackendUserManagementConfig, UserManagement } from "./hooks/useBackendUserManagement";
|
|
13
|
-
export { setApiUrl, getApiUrl, fetchAuthConfig,
|
|
13
|
+
export { setApiUrl, getApiUrl, fetchAuthConfig, AuthApiError } from "./api";
|
|
14
14
|
export type { AuthConfigResponse } from "./api";
|
package/dist/index.es.js
CHANGED
|
@@ -214,11 +214,7 @@ async function fetchAuthConfig() {
|
|
|
214
214
|
authConfigInflight = null;
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
|
|
218
|
-
authConfigCached = null;
|
|
219
|
-
authConfigInflight = null;
|
|
220
|
-
}
|
|
221
|
-
const STORAGE_KEY = "rebase_auth";
|
|
217
|
+
const STORAGE_KEY = "rebase_react_auth";
|
|
222
218
|
const TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1e3;
|
|
223
219
|
function convertToUser(userInfo) {
|
|
224
220
|
return {
|
|
@@ -276,12 +272,11 @@ function useRebaseAuthController(props = {}) {
|
|
|
276
272
|
const refreshPromiseRef = useRef(null);
|
|
277
273
|
const isMountedRef = useRef(true);
|
|
278
274
|
useEffect(() => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
setApiUrl(apiUrl);
|
|
275
|
+
const url = client?.baseUrl || apiUrl;
|
|
276
|
+
if (url) {
|
|
277
|
+
setApiUrl(url);
|
|
283
278
|
}
|
|
284
|
-
}, [client, apiUrl]);
|
|
279
|
+
}, [client, client?.baseUrl, apiUrl]);
|
|
285
280
|
const clearError = useCallback(() => {
|
|
286
281
|
setAuthProviderError(null);
|
|
287
282
|
}, []);
|
|
@@ -397,7 +392,7 @@ function useRebaseAuthController(props = {}) {
|
|
|
397
392
|
}, [initialLoading, refreshAccessToken$1, clearSessionAndSignOut]);
|
|
398
393
|
useEffect(() => {
|
|
399
394
|
if (client) {
|
|
400
|
-
client.setAuthTokenGetter(async () => {
|
|
395
|
+
client.setAuthTokenGetter?.(async () => {
|
|
401
396
|
try {
|
|
402
397
|
return await getAuthToken();
|
|
403
398
|
} catch {
|
|
@@ -430,7 +425,7 @@ function useRebaseAuthController(props = {}) {
|
|
|
430
425
|
if (defineRolesFor) {
|
|
431
426
|
const customRoles = await defineRolesFor(convertedUser);
|
|
432
427
|
if (customRoles) {
|
|
433
|
-
convertedUser = { ...convertedUser, roles: customRoles
|
|
428
|
+
convertedUser = { ...convertedUser, roles: customRoles };
|
|
434
429
|
}
|
|
435
430
|
}
|
|
436
431
|
saveAuthToStorage(tokens, userInfo);
|
|
@@ -561,7 +556,7 @@ function useRebaseAuthController(props = {}) {
|
|
|
561
556
|
if (customRoles) {
|
|
562
557
|
convertedUser = {
|
|
563
558
|
...convertedUser,
|
|
564
|
-
roles: customRoles
|
|
559
|
+
roles: customRoles
|
|
565
560
|
};
|
|
566
561
|
}
|
|
567
562
|
}
|
|
@@ -635,7 +630,7 @@ function useRebaseAuthController(props = {}) {
|
|
|
635
630
|
if (customRoles) {
|
|
636
631
|
userToSet = {
|
|
637
632
|
...userToSet,
|
|
638
|
-
roles: customRoles
|
|
633
|
+
roles: customRoles
|
|
639
634
|
};
|
|
640
635
|
}
|
|
641
636
|
}
|
|
@@ -667,12 +662,16 @@ function useRebaseAuthController(props = {}) {
|
|
|
667
662
|
if (customRoles) {
|
|
668
663
|
userToSet = {
|
|
669
664
|
...userToSet,
|
|
670
|
-
roles: customRoles
|
|
665
|
+
roles: customRoles
|
|
671
666
|
};
|
|
672
667
|
}
|
|
673
668
|
}
|
|
674
669
|
} catch (meError) {
|
|
675
670
|
if (!isMountedRef.current) return;
|
|
671
|
+
if (meError instanceof AuthApiError && (meError.code === "NOT_FOUND" || meError.code === "UNAUTHORIZED")) {
|
|
672
|
+
clearSessionAndSignOut();
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
676
675
|
userToSet = convertToUser(stored.user);
|
|
677
676
|
}
|
|
678
677
|
if (!isMountedRef.current) return;
|
|
@@ -772,10 +771,10 @@ function useRebaseAuthController(props = {}) {
|
|
|
772
771
|
emailPasswordLogin: true,
|
|
773
772
|
googleLogin: !!props.googleClientId,
|
|
774
773
|
registration: authConfig?.registrationEnabled ?? false,
|
|
775
|
-
passwordReset:
|
|
774
|
+
passwordReset: authConfig?.passwordReset ?? false,
|
|
776
775
|
sessionManagement: true,
|
|
777
776
|
profileUpdate: true,
|
|
778
|
-
emailVerification: false,
|
|
777
|
+
emailVerification: authConfig?.emailVerification ?? false,
|
|
779
778
|
enabledProviders: authConfig?.enabledProviders ?? []
|
|
780
779
|
}
|
|
781
780
|
};
|
|
@@ -792,22 +791,29 @@ function convertUser(apiUser) {
|
|
|
792
791
|
createdAt: apiUser.createdAt ? new Date(apiUser.createdAt) : null
|
|
793
792
|
};
|
|
794
793
|
}
|
|
795
|
-
function convertRole(apiRole) {
|
|
796
|
-
return {
|
|
797
|
-
id: apiRole.id,
|
|
798
|
-
name: apiRole.name,
|
|
799
|
-
isAdmin: apiRole.isAdmin ?? false,
|
|
800
|
-
config: apiRole.config ?? void 0
|
|
801
|
-
};
|
|
802
|
-
}
|
|
803
794
|
function useBackendUserManagement(config) {
|
|
804
795
|
const { client, apiUrl, getAuthToken, currentUser } = config;
|
|
805
|
-
const [
|
|
806
|
-
const [
|
|
807
|
-
const
|
|
796
|
+
const [userCache, setUserCache] = useState(/* @__PURE__ */ new Map());
|
|
797
|
+
const [hasAdminUsers, setHasAdminUsers] = useState(false);
|
|
798
|
+
const userRoles = currentUser?.roles ?? [];
|
|
799
|
+
const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
|
|
800
|
+
const [loading, setLoading] = useState(() => {
|
|
801
|
+
if (!currentUser) return false;
|
|
802
|
+
if (!isUserAdmin) return false;
|
|
803
|
+
return true;
|
|
804
|
+
});
|
|
808
805
|
const [usersError, setUsersError] = useState();
|
|
809
|
-
const [rolesError, setRolesError] = useState();
|
|
810
806
|
const lastLoadedUidRef = useRef(null);
|
|
807
|
+
const effectiveLoading = loading || !!(currentUser && isUserAdmin && lastLoadedUidRef.current !== currentUser.uid);
|
|
808
|
+
const mergeIntoCache = useCallback((incoming) => {
|
|
809
|
+
setUserCache((prev) => {
|
|
810
|
+
const next = new Map(prev);
|
|
811
|
+
for (const u of incoming) {
|
|
812
|
+
next.set(u.uid, u);
|
|
813
|
+
}
|
|
814
|
+
return next;
|
|
815
|
+
});
|
|
816
|
+
}, []);
|
|
811
817
|
const apiRequestRef = useRef(null);
|
|
812
818
|
const apiRequest = useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
|
|
813
819
|
let lastError = null;
|
|
@@ -818,7 +824,7 @@ function useBackendUserManagement(config) {
|
|
|
818
824
|
throw error;
|
|
819
825
|
}
|
|
820
826
|
try {
|
|
821
|
-
const token = getAuthToken ? await getAuthToken() : client ? await client.resolveToken() : null;
|
|
827
|
+
const token = getAuthToken ? await getAuthToken() : client?.resolveToken ? await client.resolveToken() : null;
|
|
822
828
|
const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
|
|
823
829
|
const response = await fetch(`${baseUrl}/api/admin${endpoint}`, {
|
|
824
830
|
method,
|
|
@@ -877,37 +883,29 @@ function useBackendUserManagement(config) {
|
|
|
877
883
|
throw lastError;
|
|
878
884
|
}, [apiUrl, getAuthToken]);
|
|
879
885
|
apiRequestRef.current = apiRequest;
|
|
880
|
-
useCallback(async (signal) => {
|
|
886
|
+
const checkAdminExists = useCallback(async (signal) => {
|
|
881
887
|
try {
|
|
882
|
-
const data = await apiRequest("/
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
889
|
-
}
|
|
890
|
-
}, [apiRequest]);
|
|
891
|
-
const loadUsers = useCallback(async (signal) => {
|
|
892
|
-
try {
|
|
893
|
-
const data = await apiRequest("/users", "GET", void 0, 6, signal);
|
|
894
|
-
const allUsers = data.users.map((u) => convertUser(u));
|
|
895
|
-
setUsers(allUsers);
|
|
888
|
+
const data = await apiRequest("/users?role=admin&limit=1", "GET", void 0, 6, signal);
|
|
889
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
890
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
891
|
+
if (adminUsers.length > 0) {
|
|
892
|
+
mergeIntoCache(adminUsers);
|
|
893
|
+
}
|
|
896
894
|
setUsersError(void 0);
|
|
897
895
|
} catch (error) {
|
|
898
896
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
899
|
-
console.error("Failed to
|
|
897
|
+
console.error("Failed to check admin users:", error);
|
|
900
898
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
901
899
|
}
|
|
902
|
-
}, [apiRequest]);
|
|
900
|
+
}, [apiRequest, mergeIntoCache]);
|
|
903
901
|
useEffect(() => {
|
|
904
902
|
if (!currentUser) {
|
|
905
903
|
setLoading(false);
|
|
906
904
|
return;
|
|
907
905
|
}
|
|
908
|
-
const
|
|
909
|
-
const
|
|
910
|
-
if (!
|
|
906
|
+
const userRoles2 = currentUser.roles ?? [];
|
|
907
|
+
const isUserAdmin2 = userRoles2.some((r) => r === "admin" || r === "schema-admin");
|
|
908
|
+
if (!isUserAdmin2) {
|
|
911
909
|
setLoading(false);
|
|
912
910
|
return;
|
|
913
911
|
}
|
|
@@ -919,30 +917,18 @@ function useBackendUserManagement(config) {
|
|
|
919
917
|
const load = async () => {
|
|
920
918
|
setLoading(true);
|
|
921
919
|
const request = apiRequestRef.current;
|
|
922
|
-
try {
|
|
923
|
-
const data = await request("/roles", "GET", void 0, 6, abortController.signal);
|
|
924
|
-
setRoles(data.roles.map(convertRole));
|
|
925
|
-
setRolesError(void 0);
|
|
926
|
-
} catch (error) {
|
|
927
|
-
if (error instanceof Error && error.name === "AbortError") return;
|
|
928
|
-
console.error("Failed to load roles:", error);
|
|
929
|
-
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
930
|
-
const status = error.status;
|
|
931
|
-
if (status === 403 || status === 401) {
|
|
932
|
-
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
933
|
-
setLoading(false);
|
|
934
|
-
return;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
920
|
if (!abortController.signal.aborted) {
|
|
938
921
|
try {
|
|
939
|
-
const data = await request("/users", "GET", void 0, 6, abortController.signal);
|
|
940
|
-
const
|
|
941
|
-
|
|
922
|
+
const data = await request("/users?role=admin&limit=1", "GET", void 0, 6, abortController.signal);
|
|
923
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
924
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
925
|
+
if (adminUsers.length > 0) {
|
|
926
|
+
mergeIntoCache(adminUsers);
|
|
927
|
+
}
|
|
942
928
|
setUsersError(void 0);
|
|
943
929
|
} catch (error) {
|
|
944
930
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
945
|
-
console.error("Failed to
|
|
931
|
+
console.error("Failed to check admin users:", error);
|
|
946
932
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
947
933
|
}
|
|
948
934
|
}
|
|
@@ -966,34 +952,24 @@ function useBackendUserManagement(config) {
|
|
|
966
952
|
if (options.roleId) params.set("role", options.roleId);
|
|
967
953
|
const qs = params.toString();
|
|
968
954
|
const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
|
|
955
|
+
const converted = data.users.map((u) => convertUser(u));
|
|
956
|
+
mergeIntoCache(converted);
|
|
969
957
|
return {
|
|
970
|
-
users:
|
|
958
|
+
users: converted,
|
|
971
959
|
total: data.total
|
|
972
960
|
};
|
|
973
|
-
}, [apiRequest]);
|
|
961
|
+
}, [apiRequest, mergeIntoCache]);
|
|
974
962
|
const saveUser = useCallback(async (user) => {
|
|
975
963
|
const roleIds = user.roles ?? [];
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
return updated;
|
|
986
|
-
} else {
|
|
987
|
-
const data = await apiRequest("/users", "POST", {
|
|
988
|
-
email: user.email,
|
|
989
|
-
displayName: user.displayName,
|
|
990
|
-
roles: roleIds
|
|
991
|
-
});
|
|
992
|
-
const created = convertUser(data.user);
|
|
993
|
-
setUsers((prev) => [...prev, created]);
|
|
994
|
-
return created;
|
|
995
|
-
}
|
|
996
|
-
}, [apiRequest, users, roles]);
|
|
964
|
+
const data = await apiRequest(`/users/${user.uid}`, "PUT", {
|
|
965
|
+
email: user.email,
|
|
966
|
+
displayName: user.displayName,
|
|
967
|
+
roles: roleIds
|
|
968
|
+
});
|
|
969
|
+
const updated = convertUser(data.user);
|
|
970
|
+
mergeIntoCache([updated]);
|
|
971
|
+
return updated;
|
|
972
|
+
}, [apiRequest, mergeIntoCache]);
|
|
997
973
|
const createUser = useCallback(async (user) => {
|
|
998
974
|
const roleIds = user.roles ?? [];
|
|
999
975
|
const data = await apiRequest("/users", "POST", {
|
|
@@ -1002,98 +978,78 @@ function useBackendUserManagement(config) {
|
|
|
1002
978
|
roles: roleIds
|
|
1003
979
|
});
|
|
1004
980
|
const created = convertUser(data.user);
|
|
1005
|
-
|
|
981
|
+
mergeIntoCache([created]);
|
|
1006
982
|
return {
|
|
1007
983
|
user: created,
|
|
1008
984
|
invitationSent: data.invitationSent ?? false,
|
|
1009
985
|
temporaryPassword: data.temporaryPassword
|
|
1010
986
|
};
|
|
1011
|
-
}, [apiRequest,
|
|
987
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1012
988
|
const resetPassword2 = useCallback(async (user) => {
|
|
1013
989
|
const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
|
|
1014
990
|
const updatedUser = convertUser(data.user);
|
|
1015
|
-
|
|
991
|
+
mergeIntoCache([updatedUser]);
|
|
1016
992
|
return {
|
|
1017
993
|
user: updatedUser,
|
|
1018
994
|
invitationSent: data.invitationSent ?? false,
|
|
1019
995
|
temporaryPassword: data.temporaryPassword
|
|
1020
996
|
};
|
|
1021
|
-
}, [apiRequest]);
|
|
997
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1022
998
|
const deleteUser = useCallback(async (user) => {
|
|
1023
999
|
await apiRequest(`/users/${user.uid}`, "DELETE");
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
const data = await apiRequest(`/roles/${role.id}`, "PUT", {
|
|
1030
|
-
name: role.name,
|
|
1031
|
-
isAdmin: role.isAdmin,
|
|
1032
|
-
config: role.config
|
|
1033
|
-
});
|
|
1034
|
-
const updated = convertRole(data.role);
|
|
1035
|
-
setRoles((prev) => prev.map((r) => r.id === updated.id ? updated : r));
|
|
1036
|
-
} else {
|
|
1037
|
-
const data = await apiRequest("/roles", "POST", {
|
|
1038
|
-
id: role.id,
|
|
1039
|
-
name: role.name,
|
|
1040
|
-
isAdmin: role.isAdmin ?? false,
|
|
1041
|
-
config: role.config
|
|
1042
|
-
});
|
|
1043
|
-
const created = convertRole(data.role);
|
|
1044
|
-
setRoles((prev) => [...prev, created]);
|
|
1045
|
-
}
|
|
1046
|
-
}, [apiRequest, roles]);
|
|
1047
|
-
const deleteRole = useCallback(async (role) => {
|
|
1048
|
-
await apiRequest(`/roles/${role.id}`, "DELETE");
|
|
1049
|
-
setRoles((prev) => prev.filter((r) => r.id !== role.id));
|
|
1000
|
+
setUserCache((prev) => {
|
|
1001
|
+
const next = new Map(prev);
|
|
1002
|
+
next.delete(user.uid);
|
|
1003
|
+
return next;
|
|
1004
|
+
});
|
|
1050
1005
|
}, [apiRequest]);
|
|
1051
1006
|
const getUser = useCallback((uid) => {
|
|
1052
|
-
return
|
|
1053
|
-
}, [
|
|
1007
|
+
return userCache.get(uid) ?? null;
|
|
1008
|
+
}, [userCache]);
|
|
1054
1009
|
const defineRolesFor = useCallback(async (user) => {
|
|
1055
|
-
|
|
1056
|
-
if (!existingUser)
|
|
1010
|
+
let existingUser = userCache.get(user.uid) ?? Array.from(userCache.values()).find((u) => u.email === user.email);
|
|
1011
|
+
if (!existingUser) {
|
|
1012
|
+
try {
|
|
1013
|
+
const data = await apiRequest(`/users/${user.uid}`, "GET");
|
|
1014
|
+
existingUser = convertUser(data.user);
|
|
1015
|
+
mergeIntoCache([existingUser]);
|
|
1016
|
+
} catch {
|
|
1017
|
+
return void 0;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1057
1020
|
const userRoleIds = existingUser.roles ?? [];
|
|
1058
|
-
return
|
|
1059
|
-
}, [
|
|
1021
|
+
return userRoleIds;
|
|
1022
|
+
}, [userCache, apiRequest, mergeIntoCache]);
|
|
1060
1023
|
const isAdmin = currentUser?.roles?.includes("admin") ?? false;
|
|
1061
1024
|
const bootstrapAdmin = useCallback(async () => {
|
|
1062
1025
|
try {
|
|
1063
1026
|
await apiRequest("/bootstrap", "POST");
|
|
1064
|
-
|
|
1065
|
-
const loadedRoles = data.roles.map(convertRole);
|
|
1066
|
-
setRoles(loadedRoles);
|
|
1067
|
-
await loadUsers();
|
|
1027
|
+
await checkAdminExists();
|
|
1068
1028
|
} catch (error) {
|
|
1069
1029
|
console.error("Failed to bootstrap admin:", error);
|
|
1070
1030
|
throw error;
|
|
1071
1031
|
}
|
|
1072
|
-
}, [apiRequest,
|
|
1032
|
+
}, [apiRequest, checkAdminExists]);
|
|
1033
|
+
const users = Array.from(userCache.values());
|
|
1073
1034
|
return {
|
|
1074
|
-
loading,
|
|
1035
|
+
loading: effectiveLoading,
|
|
1075
1036
|
users,
|
|
1037
|
+
hasAdminUsers,
|
|
1076
1038
|
saveUser,
|
|
1077
1039
|
createUser,
|
|
1078
1040
|
resetPassword: resetPassword2,
|
|
1079
1041
|
deleteUser,
|
|
1080
|
-
roles,
|
|
1081
|
-
saveRole,
|
|
1082
|
-
deleteRole,
|
|
1083
1042
|
isAdmin,
|
|
1084
1043
|
allowDefaultRolesCreation: isAdmin,
|
|
1085
|
-
includeCollectionConfigPermissions: true,
|
|
1086
1044
|
defineRolesFor,
|
|
1087
1045
|
getUser,
|
|
1088
1046
|
searchUsers,
|
|
1089
1047
|
usersError,
|
|
1090
|
-
rolesError,
|
|
1091
1048
|
bootstrapAdmin
|
|
1092
1049
|
};
|
|
1093
1050
|
}
|
|
1094
1051
|
export {
|
|
1095
1052
|
AuthApiError,
|
|
1096
|
-
clearAuthConfigCache,
|
|
1097
1053
|
fetchAuthConfig,
|
|
1098
1054
|
getApiUrl,
|
|
1099
1055
|
setApiUrl,
|