@fluxbase/sdk-react 0.1.0-rc.1 → 2026.1.1-rc.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/README-ADMIN.md +8 -8
- package/README.md +27 -14
- package/dist/index.d.mts +713 -83
- package/dist/index.d.ts +713 -83
- package/dist/index.js +574 -175
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +561 -172
- package/dist/index.mjs.map +1 -1
- package/examples/AdminDashboard.tsx +3 -3
- package/examples/README.md +1 -1
- package/package.json +3 -3
- package/src/index.ts +60 -18
- package/src/use-admin-auth.ts +62 -51
- package/src/use-auth-config.ts +101 -0
- package/src/use-auth.ts +66 -49
- package/src/use-captcha.ts +250 -0
- package/src/{use-api-keys.ts → use-client-keys.ts} +43 -32
- package/src/use-graphql.ts +392 -0
- package/src/use-realtime.ts +58 -44
- package/src/use-saml.ts +221 -0
- package/src/use-storage.ts +325 -82
- package/src/use-users.ts +11 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/typedoc.json +2 -4
- package/CHANGELOG.md +0 -67
- package/src/use-rpc.ts +0 -109
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CAPTCHA hooks for Fluxbase SDK
|
|
3
|
+
*
|
|
4
|
+
* Provides hooks to:
|
|
5
|
+
* - Fetch CAPTCHA configuration from the server
|
|
6
|
+
* - Manage CAPTCHA widget state
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useQuery } from "@tanstack/react-query";
|
|
10
|
+
import { useFluxbaseClient } from "./context";
|
|
11
|
+
import type { CaptchaConfig, CaptchaProvider } from "@fluxbase/sdk";
|
|
12
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Hook to get the CAPTCHA configuration from the server
|
|
16
|
+
* Use this to determine which CAPTCHA provider to load
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* function AuthPage() {
|
|
21
|
+
* const { data: captchaConfig, isLoading } = useCaptchaConfig();
|
|
22
|
+
*
|
|
23
|
+
* if (isLoading) return <Loading />;
|
|
24
|
+
*
|
|
25
|
+
* return captchaConfig?.enabled ? (
|
|
26
|
+
* <CaptchaWidget provider={captchaConfig.provider} siteKey={captchaConfig.site_key} />
|
|
27
|
+
* ) : null;
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function useCaptchaConfig() {
|
|
32
|
+
const client = useFluxbaseClient();
|
|
33
|
+
|
|
34
|
+
return useQuery<CaptchaConfig>({
|
|
35
|
+
queryKey: ["fluxbase", "auth", "captcha", "config"],
|
|
36
|
+
queryFn: async () => {
|
|
37
|
+
const { data, error } = await client.auth.getCaptchaConfig();
|
|
38
|
+
if (error) {
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
return data!;
|
|
42
|
+
},
|
|
43
|
+
staleTime: 1000 * 60 * 60, // Cache for 1 hour (config rarely changes)
|
|
44
|
+
gcTime: 1000 * 60 * 60 * 24, // Keep in cache for 24 hours
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* CAPTCHA widget state for managing token generation
|
|
50
|
+
*/
|
|
51
|
+
export interface CaptchaState {
|
|
52
|
+
/** Current CAPTCHA token (null until solved) */
|
|
53
|
+
token: string | null;
|
|
54
|
+
/** Whether the CAPTCHA widget is ready */
|
|
55
|
+
isReady: boolean;
|
|
56
|
+
/** Whether a token is being generated */
|
|
57
|
+
isLoading: boolean;
|
|
58
|
+
/** Any error that occurred */
|
|
59
|
+
error: Error | null;
|
|
60
|
+
/** Reset the CAPTCHA widget */
|
|
61
|
+
reset: () => void;
|
|
62
|
+
/** Execute/trigger the CAPTCHA (for invisible CAPTCHA like reCAPTCHA v3) */
|
|
63
|
+
execute: () => Promise<string>;
|
|
64
|
+
/** Callback to be called when CAPTCHA is verified */
|
|
65
|
+
onVerify: (token: string) => void;
|
|
66
|
+
/** Callback to be called when CAPTCHA expires */
|
|
67
|
+
onExpire: () => void;
|
|
68
|
+
/** Callback to be called when CAPTCHA errors */
|
|
69
|
+
onError: (error: Error) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Hook to manage CAPTCHA widget state
|
|
74
|
+
*
|
|
75
|
+
* This hook provides a standardized interface for managing CAPTCHA tokens
|
|
76
|
+
* across different providers (hCaptcha, reCAPTCHA v3, Turnstile, Cap).
|
|
77
|
+
*
|
|
78
|
+
* Supported providers:
|
|
79
|
+
* - hcaptcha: Privacy-focused visual challenge
|
|
80
|
+
* - recaptcha_v3: Google's invisible risk-based CAPTCHA
|
|
81
|
+
* - turnstile: Cloudflare's invisible CAPTCHA
|
|
82
|
+
* - cap: Self-hosted proof-of-work CAPTCHA (https://capjs.js.org/)
|
|
83
|
+
*
|
|
84
|
+
* @param provider - The CAPTCHA provider type
|
|
85
|
+
* @returns CAPTCHA state and callbacks
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```tsx
|
|
89
|
+
* function LoginForm() {
|
|
90
|
+
* const captcha = useCaptcha('hcaptcha');
|
|
91
|
+
*
|
|
92
|
+
* const handleSubmit = async (e: FormEvent) => {
|
|
93
|
+
* e.preventDefault();
|
|
94
|
+
*
|
|
95
|
+
* // Get CAPTCHA token
|
|
96
|
+
* const captchaToken = captcha.token || await captcha.execute();
|
|
97
|
+
*
|
|
98
|
+
* // Sign in with CAPTCHA token
|
|
99
|
+
* await signIn({
|
|
100
|
+
* email,
|
|
101
|
+
* password,
|
|
102
|
+
* captchaToken
|
|
103
|
+
* });
|
|
104
|
+
* };
|
|
105
|
+
*
|
|
106
|
+
* return (
|
|
107
|
+
* <form onSubmit={handleSubmit}>
|
|
108
|
+
* <input name="email" />
|
|
109
|
+
* <input name="password" type="password" />
|
|
110
|
+
*
|
|
111
|
+
* <HCaptcha
|
|
112
|
+
* sitekey={siteKey}
|
|
113
|
+
* onVerify={captcha.onVerify}
|
|
114
|
+
* onExpire={captcha.onExpire}
|
|
115
|
+
* onError={captcha.onError}
|
|
116
|
+
* />
|
|
117
|
+
*
|
|
118
|
+
* <button type="submit" disabled={!captcha.isReady}>
|
|
119
|
+
* Sign In
|
|
120
|
+
* </button>
|
|
121
|
+
* </form>
|
|
122
|
+
* );
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @example Cap provider
|
|
127
|
+
* ```tsx
|
|
128
|
+
* function LoginForm() {
|
|
129
|
+
* const { data: config } = useCaptchaConfig();
|
|
130
|
+
* const captcha = useCaptcha(config?.provider);
|
|
131
|
+
*
|
|
132
|
+
* // For Cap, load the widget from cap_server_url
|
|
133
|
+
* // <script src={`${config.cap_server_url}/widget.js`} />
|
|
134
|
+
* // <cap-widget data-cap-url={config.cap_server_url} />
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export function useCaptcha(provider?: CaptchaProvider): CaptchaState {
|
|
139
|
+
const [token, setToken] = useState<string | null>(null);
|
|
140
|
+
const [isReady, setIsReady] = useState(false);
|
|
141
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
142
|
+
const [error, setError] = useState<Error | null>(null);
|
|
143
|
+
|
|
144
|
+
// Promise resolver for execute() method
|
|
145
|
+
const executeResolverRef = useRef<((token: string) => void) | null>(null);
|
|
146
|
+
const executeRejecterRef = useRef<((error: Error) => void) | null>(null);
|
|
147
|
+
|
|
148
|
+
// Callback when CAPTCHA is verified
|
|
149
|
+
const onVerify = useCallback((newToken: string) => {
|
|
150
|
+
setToken(newToken);
|
|
151
|
+
setIsLoading(false);
|
|
152
|
+
setError(null);
|
|
153
|
+
setIsReady(true);
|
|
154
|
+
|
|
155
|
+
// Resolve the execute() promise if waiting
|
|
156
|
+
if (executeResolverRef.current) {
|
|
157
|
+
executeResolverRef.current(newToken);
|
|
158
|
+
executeResolverRef.current = null;
|
|
159
|
+
executeRejecterRef.current = null;
|
|
160
|
+
}
|
|
161
|
+
}, []);
|
|
162
|
+
|
|
163
|
+
// Callback when CAPTCHA expires
|
|
164
|
+
const onExpire = useCallback(() => {
|
|
165
|
+
setToken(null);
|
|
166
|
+
setIsReady(true);
|
|
167
|
+
}, []);
|
|
168
|
+
|
|
169
|
+
// Callback when CAPTCHA errors
|
|
170
|
+
const onError = useCallback((err: Error) => {
|
|
171
|
+
setError(err);
|
|
172
|
+
setIsLoading(false);
|
|
173
|
+
setToken(null);
|
|
174
|
+
|
|
175
|
+
// Reject the execute() promise if waiting
|
|
176
|
+
if (executeRejecterRef.current) {
|
|
177
|
+
executeRejecterRef.current(err);
|
|
178
|
+
executeResolverRef.current = null;
|
|
179
|
+
executeRejecterRef.current = null;
|
|
180
|
+
}
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
183
|
+
// Reset the CAPTCHA
|
|
184
|
+
const reset = useCallback(() => {
|
|
185
|
+
setToken(null);
|
|
186
|
+
setError(null);
|
|
187
|
+
setIsLoading(false);
|
|
188
|
+
}, []);
|
|
189
|
+
|
|
190
|
+
// Execute/trigger the CAPTCHA (for invisible CAPTCHA)
|
|
191
|
+
const execute = useCallback(async (): Promise<string> => {
|
|
192
|
+
// If we already have a token, return it
|
|
193
|
+
if (token) {
|
|
194
|
+
return token;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// If CAPTCHA is not configured, return empty string
|
|
198
|
+
if (!provider) {
|
|
199
|
+
return "";
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
setIsLoading(true);
|
|
203
|
+
setError(null);
|
|
204
|
+
|
|
205
|
+
// Return a promise that will be resolved by onVerify
|
|
206
|
+
return new Promise<string>((resolve, reject) => {
|
|
207
|
+
executeResolverRef.current = resolve;
|
|
208
|
+
executeRejecterRef.current = reject;
|
|
209
|
+
|
|
210
|
+
// For invisible CAPTCHAs, the widget should call onVerify when done
|
|
211
|
+
// The actual execution is handled by the CAPTCHA widget component
|
|
212
|
+
});
|
|
213
|
+
}, [token, provider]);
|
|
214
|
+
|
|
215
|
+
// Mark as ready when provider is set
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
if (provider) {
|
|
218
|
+
setIsReady(true);
|
|
219
|
+
}
|
|
220
|
+
}, [provider]);
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
token,
|
|
224
|
+
isReady,
|
|
225
|
+
isLoading,
|
|
226
|
+
error,
|
|
227
|
+
reset,
|
|
228
|
+
execute,
|
|
229
|
+
onVerify,
|
|
230
|
+
onExpire,
|
|
231
|
+
onError,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Check if CAPTCHA is required for a specific endpoint
|
|
237
|
+
*
|
|
238
|
+
* @param config - CAPTCHA configuration from useCaptchaConfig
|
|
239
|
+
* @param endpoint - The endpoint to check (e.g., 'signup', 'login', 'password_reset')
|
|
240
|
+
* @returns Whether CAPTCHA is required for this endpoint
|
|
241
|
+
*/
|
|
242
|
+
export function isCaptchaRequiredForEndpoint(
|
|
243
|
+
config: CaptchaConfig | undefined,
|
|
244
|
+
endpoint: string
|
|
245
|
+
): boolean {
|
|
246
|
+
if (!config?.enabled) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
return config.endpoints?.includes(endpoint) ?? false;
|
|
250
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from 'react'
|
|
2
2
|
import { useFluxbaseClient } from './context'
|
|
3
|
-
import type {
|
|
3
|
+
import type { ClientKey, CreateClientKeyRequest } from '@fluxbase/sdk'
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface UseClientKeysOptions {
|
|
6
6
|
/**
|
|
7
|
-
* Whether to automatically fetch
|
|
7
|
+
* Whether to automatically fetch client keys on mount
|
|
8
8
|
* @default true
|
|
9
9
|
*/
|
|
10
10
|
autoFetch?: boolean
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export interface
|
|
13
|
+
export interface UseClientKeysReturn {
|
|
14
14
|
/**
|
|
15
|
-
* Array of
|
|
15
|
+
* Array of client keys
|
|
16
16
|
*/
|
|
17
|
-
keys:
|
|
17
|
+
keys: ClientKey[]
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Whether keys are being fetched
|
|
@@ -27,45 +27,45 @@ export interface UseAPIKeysReturn {
|
|
|
27
27
|
error: Error | null
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Refetch
|
|
30
|
+
* Refetch client keys
|
|
31
31
|
*/
|
|
32
32
|
refetch: () => Promise<void>
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Create a new
|
|
35
|
+
* Create a new client key
|
|
36
36
|
*/
|
|
37
|
-
createKey: (request:
|
|
37
|
+
createKey: (request: CreateClientKeyRequest) => Promise<{ key: string; keyData: ClientKey }>
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Update
|
|
40
|
+
* Update a client key
|
|
41
41
|
*/
|
|
42
42
|
updateKey: (keyId: string, update: { name?: string; description?: string }) => Promise<void>
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
* Revoke
|
|
45
|
+
* Revoke a client key
|
|
46
46
|
*/
|
|
47
47
|
revokeKey: (keyId: string) => Promise<void>
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Delete
|
|
50
|
+
* Delete a client key
|
|
51
51
|
*/
|
|
52
52
|
deleteKey: (keyId: string) => Promise<void>
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Hook for managing
|
|
56
|
+
* Hook for managing client keys
|
|
57
57
|
*
|
|
58
|
-
* Provides
|
|
58
|
+
* Provides client key list and management functions.
|
|
59
59
|
*
|
|
60
60
|
* @example
|
|
61
61
|
* ```tsx
|
|
62
|
-
* function
|
|
63
|
-
* const { keys, isLoading, createKey, revokeKey } =
|
|
62
|
+
* function ClientKeyManager() {
|
|
63
|
+
* const { keys, isLoading, createKey, revokeKey } = useClientKeys()
|
|
64
64
|
*
|
|
65
65
|
* const handleCreate = async () => {
|
|
66
66
|
* const { key, keyData } = await createKey({
|
|
67
67
|
* name: 'Backend Service',
|
|
68
|
-
* description: '
|
|
68
|
+
* description: 'Client key for backend',
|
|
69
69
|
* expires_at: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString()
|
|
70
70
|
* })
|
|
71
71
|
* alert(`Key created: ${key}`)
|
|
@@ -85,23 +85,23 @@ export interface UseAPIKeysReturn {
|
|
|
85
85
|
* }
|
|
86
86
|
* ```
|
|
87
87
|
*/
|
|
88
|
-
export function
|
|
88
|
+
export function useClientKeys(options: UseClientKeysOptions = {}): UseClientKeysReturn {
|
|
89
89
|
const { autoFetch = true } = options
|
|
90
90
|
const client = useFluxbaseClient()
|
|
91
91
|
|
|
92
|
-
const [keys, setKeys] = useState<
|
|
92
|
+
const [keys, setKeys] = useState<ClientKey[]>([])
|
|
93
93
|
const [isLoading, setIsLoading] = useState(autoFetch)
|
|
94
94
|
const [error, setError] = useState<Error | null>(null)
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
|
-
* Fetch
|
|
97
|
+
* Fetch client keys from API
|
|
98
98
|
*/
|
|
99
99
|
const fetchKeys = useCallback(async () => {
|
|
100
100
|
try {
|
|
101
101
|
setIsLoading(true)
|
|
102
102
|
setError(null)
|
|
103
|
-
const response = await client.admin.management.
|
|
104
|
-
setKeys(response.
|
|
103
|
+
const response = await client.admin.management.clientKeys.list()
|
|
104
|
+
setKeys(response.client_keys)
|
|
105
105
|
} catch (err) {
|
|
106
106
|
setError(err as Error)
|
|
107
107
|
} finally {
|
|
@@ -110,45 +110,45 @@ export function useAPIKeys(options: UseAPIKeysOptions = {}): UseAPIKeysReturn {
|
|
|
110
110
|
}, [client])
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
|
-
* Create a new
|
|
113
|
+
* Create a new client key
|
|
114
114
|
*/
|
|
115
115
|
const createKey = useCallback(
|
|
116
|
-
async (request:
|
|
117
|
-
const response = await client.admin.management.
|
|
116
|
+
async (request: CreateClientKeyRequest): Promise<{ key: string; keyData: ClientKey }> => {
|
|
117
|
+
const response = await client.admin.management.clientKeys.create(request)
|
|
118
118
|
await fetchKeys() // Refresh list
|
|
119
|
-
return { key: response.key, keyData: response.
|
|
119
|
+
return { key: response.key, keyData: response.client_key }
|
|
120
120
|
},
|
|
121
121
|
[client, fetchKeys]
|
|
122
122
|
)
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* Update
|
|
125
|
+
* Update a client key
|
|
126
126
|
*/
|
|
127
127
|
const updateKey = useCallback(
|
|
128
128
|
async (keyId: string, update: { name?: string; description?: string }): Promise<void> => {
|
|
129
|
-
await client.admin.management.
|
|
129
|
+
await client.admin.management.clientKeys.update(keyId, update)
|
|
130
130
|
await fetchKeys() // Refresh list
|
|
131
131
|
},
|
|
132
132
|
[client, fetchKeys]
|
|
133
133
|
)
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
* Revoke
|
|
136
|
+
* Revoke a client key
|
|
137
137
|
*/
|
|
138
138
|
const revokeKey = useCallback(
|
|
139
139
|
async (keyId: string): Promise<void> => {
|
|
140
|
-
await client.admin.management.
|
|
140
|
+
await client.admin.management.clientKeys.revoke(keyId)
|
|
141
141
|
await fetchKeys() // Refresh list
|
|
142
142
|
},
|
|
143
143
|
[client, fetchKeys]
|
|
144
144
|
)
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
* Delete
|
|
147
|
+
* Delete a client key
|
|
148
148
|
*/
|
|
149
149
|
const deleteKey = useCallback(
|
|
150
150
|
async (keyId: string): Promise<void> => {
|
|
151
|
-
await client.admin.management.
|
|
151
|
+
await client.admin.management.clientKeys.delete(keyId)
|
|
152
152
|
await fetchKeys() // Refresh list
|
|
153
153
|
},
|
|
154
154
|
[client, fetchKeys]
|
|
@@ -172,3 +172,14 @@ export function useAPIKeys(options: UseAPIKeysOptions = {}): UseAPIKeysReturn {
|
|
|
172
172
|
deleteKey
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @deprecated Use useClientKeys instead
|
|
178
|
+
*/
|
|
179
|
+
export const useAPIKeys = useClientKeys
|
|
180
|
+
|
|
181
|
+
/** @deprecated Use UseClientKeysOptions instead */
|
|
182
|
+
export type UseAPIKeysOptions = UseClientKeysOptions
|
|
183
|
+
|
|
184
|
+
/** @deprecated Use UseClientKeysReturn instead */
|
|
185
|
+
export type UseAPIKeysReturn = UseClientKeysReturn
|