@djangocfg/api 2.1.103 → 2.1.105
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/auth.cjs +3348 -3218
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +77 -1
- package/dist/auth.d.ts +77 -1
- package/dist/auth.mjs +3295 -3165
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +95 -64
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.d.cts +134 -21
- package/dist/clients.d.ts +134 -21
- package/dist/clients.mjs +95 -64
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +1 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.mjs +1 -0
- package/dist/hooks.mjs.map +1 -1
- package/package.json +2 -3
- package/src/auth/context/AuthContext.tsx +1 -1
- package/src/auth/hooks/index.ts +4 -0
- package/src/auth/hooks/useAuthGuard.ts +1 -1
- package/src/auth/hooks/useAutoAuth.ts +2 -1
- package/src/auth/hooks/useCfgRouter.ts +153 -0
- package/src/auth/hooks/useGithubAuth.ts +1 -1
- package/src/auth/hooks/useQueryParams.ts +73 -0
- package/src/auth/hooks/useTwoFactor.ts +1 -1
- package/src/auth/hooks/useTwoFactorStatus.ts +5 -1
- package/src/generated/cfg_totp/CLAUDE.md +1 -1
- package/src/generated/cfg_totp/_utils/fetchers/totp__totp_management.ts +3 -3
- package/src/generated/cfg_totp/_utils/hooks/totp__totp_management.ts +3 -3
- package/src/generated/cfg_totp/_utils/schemas/DeviceListResponse.schema.ts +21 -0
- package/src/generated/cfg_totp/_utils/schemas/{PaginatedDeviceListList.schema.ts → PaginatedDeviceListResponseList.schema.ts} +5 -5
- package/src/generated/cfg_totp/_utils/schemas/TotpVerifyUser.schema.ts +34 -0
- package/src/generated/cfg_totp/_utils/schemas/VerifyResponse.schema.ts +2 -1
- package/src/generated/cfg_totp/_utils/schemas/index.ts +3 -1
- package/src/generated/cfg_totp/schema.json +124 -5
- package/src/generated/cfg_totp/totp__totp_management/client.ts +3 -3
- package/src/generated/cfg_totp/totp__totp_management/models.ts +12 -2
- package/src/generated/cfg_totp/totp__totp_verification/models.ts +30 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/api",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.105",
|
|
4
4
|
"description": "Auto-generated TypeScript API client with React hooks, SWR integration, and Zod validation for Django REST Framework backends",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"django",
|
|
@@ -74,7 +74,6 @@
|
|
|
74
74
|
"check": "tsc --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"@djangocfg/ui-nextjs": "^2.1.103",
|
|
78
77
|
"consola": "^3.4.2",
|
|
79
78
|
"next": ">=16.0.0",
|
|
80
79
|
"p-retry": "^7.0.0",
|
|
@@ -85,7 +84,7 @@
|
|
|
85
84
|
"devDependencies": {
|
|
86
85
|
"@types/node": "^24.7.2",
|
|
87
86
|
"@types/react": "^19.0.0",
|
|
88
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
87
|
+
"@djangocfg/typescript-config": "^2.1.105",
|
|
89
88
|
"next": "^16.0.0",
|
|
90
89
|
"react": "^19.0.0",
|
|
91
90
|
"tsup": "^8.5.0",
|
|
@@ -6,7 +6,7 @@ import React, {
|
|
|
6
6
|
createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState
|
|
7
7
|
} from 'react';
|
|
8
8
|
|
|
9
|
-
import { useCfgRouter, useLocalStorage, useQueryParams } from '
|
|
9
|
+
import { useCfgRouter, useLocalStorage, useQueryParams } from '../hooks';
|
|
10
10
|
|
|
11
11
|
import { api as apiAccounts, Enums } from '../../';
|
|
12
12
|
import { clearProfileCache, getCachedProfile } from '../hooks/useProfileCache';
|
package/src/auth/hooks/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
// Router hooks
|
|
4
|
+
export { useCfgRouter } from './useCfgRouter';
|
|
5
|
+
export { useQueryParams } from './useQueryParams';
|
|
6
|
+
|
|
3
7
|
// Core form hooks (decomposed)
|
|
4
8
|
export { useAuthFormState, type UseAuthFormStateReturn } from './useAuthFormState';
|
|
5
9
|
export { useAuthValidation, detectChannelFromIdentifier, validateIdentifier } from './useAuthValidation';
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import { usePathname } from 'next/navigation';
|
|
4
4
|
import { useEffect } from 'react';
|
|
5
5
|
|
|
6
|
-
import { useCfgRouter
|
|
6
|
+
import { useCfgRouter } from './useCfgRouter';
|
|
7
|
+
import { useQueryParams } from './useQueryParams';
|
|
7
8
|
|
|
8
9
|
import { authLogger } from '../utils/logger';
|
|
9
10
|
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal Router Hook with BasePath Support
|
|
3
|
+
*
|
|
4
|
+
* Wrapper around Next.js useRouter that automatically handles basePath
|
|
5
|
+
* for static builds served via iframe or subdirectory
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: In Next.js 15 App Router, router.push() does NOT automatically
|
|
8
|
+
* handle basePath (unlike Pages Router). This is a breaking change in App Router.
|
|
9
|
+
*
|
|
10
|
+
* This hook ensures basePath is always included when navigating, especially
|
|
11
|
+
* important for static exports served via iframe where basePath is critical.
|
|
12
|
+
*
|
|
13
|
+
* @see https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use client';
|
|
17
|
+
|
|
18
|
+
import { useRouter as useNextRouter } from 'next/navigation';
|
|
19
|
+
import { useCallback, useMemo } from 'react';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get base path from environment variable
|
|
23
|
+
*/
|
|
24
|
+
function getBasePath(): string {
|
|
25
|
+
if (typeof process === 'undefined') {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
return process.env.NEXT_PUBLIC_BASE_PATH || '';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Add base path to a route path
|
|
33
|
+
*/
|
|
34
|
+
function withBasePath(path: string, basePath: string): string {
|
|
35
|
+
if (!basePath) {
|
|
36
|
+
return path;
|
|
37
|
+
}
|
|
38
|
+
// Ensure path starts with /
|
|
39
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
40
|
+
// Remove trailing slash from basePath
|
|
41
|
+
const normalizedBasePath = basePath.replace(/\/$/, '');
|
|
42
|
+
return `${normalizedBasePath}${normalizedPath}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Router with basePath support
|
|
47
|
+
*
|
|
48
|
+
* Automatically adds basePath to all navigation methods when basePath is configured.
|
|
49
|
+
* In Next.js 15 App Router, router.push() doesn't handle basePath automatically,
|
|
50
|
+
* so this hook uses window.location to ensure basePath is always included.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* const router = useCfgRouter();
|
|
55
|
+
*
|
|
56
|
+
* // With basePath='/cfg/admin':
|
|
57
|
+
* router.push('/dashboard'); // Client-side navigation to '/cfg/admin/dashboard'
|
|
58
|
+
* router.replace('/auth'); // Client-side replace with '/cfg/admin/auth'
|
|
59
|
+
* router.hardPush('/dashboard'); // Full page reload to '/cfg/admin/dashboard'
|
|
60
|
+
* router.hardReplace('/auth'); // Full page replace with '/cfg/admin/auth'
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function useCfgRouter() {
|
|
64
|
+
const router = useNextRouter();
|
|
65
|
+
|
|
66
|
+
// Get basePath and check if we're in static build mode
|
|
67
|
+
const basePath = useMemo(() => getBasePath(), []);
|
|
68
|
+
const isStaticBuild = useMemo(() => {
|
|
69
|
+
return typeof process !== 'undefined' && process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
const push = useCallback((href: string, options?: { scroll?: boolean }) => {
|
|
73
|
+
if (basePath) {
|
|
74
|
+
// App Router doesn't handle basePath automatically, use window.location
|
|
75
|
+
window.location.href = withBasePath(href, basePath);
|
|
76
|
+
} else {
|
|
77
|
+
// No basePath configured, use standard router
|
|
78
|
+
router.push(href, options);
|
|
79
|
+
}
|
|
80
|
+
}, [router, basePath]);
|
|
81
|
+
|
|
82
|
+
const replace = useCallback((href: string, options?: { scroll?: boolean }) => {
|
|
83
|
+
if (basePath) {
|
|
84
|
+
// App Router doesn't handle basePath automatically, use window.location
|
|
85
|
+
window.location.replace(withBasePath(href, basePath));
|
|
86
|
+
} else {
|
|
87
|
+
// No basePath configured, use standard router
|
|
88
|
+
router.replace(href, options);
|
|
89
|
+
}
|
|
90
|
+
}, [router, basePath]);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Hard push - always uses window.location.href for full page reload
|
|
94
|
+
*
|
|
95
|
+
* Use this for auth redirects where React contexts need to reinitialize.
|
|
96
|
+
* Unlike push(), this ALWAYS triggers a full page reload, ensuring all
|
|
97
|
+
* contexts are reinitialized with fresh state.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```tsx
|
|
101
|
+
* // After successful login - contexts need to reload with new auth state
|
|
102
|
+
* router.hardPush('/dashboard');
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
const hardPush = useCallback((href: string) => {
|
|
106
|
+
window.location.href = withBasePath(href, basePath);
|
|
107
|
+
}, [basePath]);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Hard replace - always uses window.location.replace for full page reload
|
|
111
|
+
*
|
|
112
|
+
* Same as hardPush but replaces current history entry.
|
|
113
|
+
* Use for auth redirects where you don't want back button to return to login.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```tsx
|
|
117
|
+
* // After logout - replace so back button doesn't go to protected page
|
|
118
|
+
* router.hardReplace('/auth');
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
const hardReplace = useCallback((href: string) => {
|
|
122
|
+
window.location.replace(withBasePath(href, basePath));
|
|
123
|
+
}, [basePath]);
|
|
124
|
+
|
|
125
|
+
const prefetch = useCallback((href: string) => {
|
|
126
|
+
// Prefetch doesn't need basePath handling, Next.js handles it
|
|
127
|
+
router.prefetch(href);
|
|
128
|
+
}, [router]);
|
|
129
|
+
|
|
130
|
+
const back = useCallback(() => {
|
|
131
|
+
router.back();
|
|
132
|
+
}, [router]);
|
|
133
|
+
|
|
134
|
+
const forward = useCallback(() => {
|
|
135
|
+
router.forward();
|
|
136
|
+
}, [router]);
|
|
137
|
+
|
|
138
|
+
const refresh = useCallback(() => {
|
|
139
|
+
router.refresh();
|
|
140
|
+
}, [router]);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
push,
|
|
144
|
+
replace,
|
|
145
|
+
hardPush,
|
|
146
|
+
hardReplace,
|
|
147
|
+
prefetch,
|
|
148
|
+
back,
|
|
149
|
+
forward,
|
|
150
|
+
refresh,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useState } from 'react';
|
|
4
4
|
|
|
5
|
-
import { useCfgRouter } from '
|
|
5
|
+
import { useCfgRouter } from './useCfgRouter';
|
|
6
6
|
|
|
7
7
|
import { apiAccounts } from '../../clients';
|
|
8
8
|
import { Analytics, AnalyticsCategory, AnalyticsEvent } from '../utils/analytics';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useQueryParams Hook
|
|
3
|
+
*
|
|
4
|
+
* Safe hook to access URL query parameters without requiring Suspense boundary.
|
|
5
|
+
* Works on client-side only, returns empty URLSearchParams during SSR/prerendering.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const params = useQueryParams();
|
|
10
|
+
* const flow = params.get('flow');
|
|
11
|
+
* const hasFlow = params.has('flow');
|
|
12
|
+
* const allTags = params.getAll('tags');
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use client';
|
|
17
|
+
|
|
18
|
+
import { usePathname } from 'next/navigation';
|
|
19
|
+
import { useEffect, useRef, useState } from 'react';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Hook to safely access URL query parameters without useSearchParams()
|
|
23
|
+
*
|
|
24
|
+
* This hook reads query parameters directly from window.location.search,
|
|
25
|
+
* avoiding the need for Suspense boundaries that useSearchParams() requires.
|
|
26
|
+
*
|
|
27
|
+
* Automatically updates when URL changes (navigation, back/forward, etc.)
|
|
28
|
+
* Uses pathname from Next.js to detect route changes and polls for query param changes.
|
|
29
|
+
*
|
|
30
|
+
* Returns a URLSearchParams object with get(), getAll(), has(), etc.
|
|
31
|
+
*
|
|
32
|
+
* @returns URLSearchParams object (empty during SSR)
|
|
33
|
+
*/
|
|
34
|
+
export function useQueryParams(): URLSearchParams {
|
|
35
|
+
const pathname = usePathname();
|
|
36
|
+
const [queryParams, setQueryParams] = useState<URLSearchParams>(() => {
|
|
37
|
+
if (typeof window === 'undefined') {
|
|
38
|
+
return new URLSearchParams();
|
|
39
|
+
}
|
|
40
|
+
return new URLSearchParams(window.location.search);
|
|
41
|
+
});
|
|
42
|
+
const lastSearchRef = useRef<string>('');
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (typeof window === 'undefined') return;
|
|
46
|
+
|
|
47
|
+
const updateQueryParams = () => {
|
|
48
|
+
const currentSearch = window.location.search;
|
|
49
|
+
if (currentSearch !== lastSearchRef.current) {
|
|
50
|
+
lastSearchRef.current = currentSearch;
|
|
51
|
+
setQueryParams(new URLSearchParams(currentSearch));
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Update when pathname changes (Next.js navigation)
|
|
56
|
+
updateQueryParams();
|
|
57
|
+
|
|
58
|
+
// Listen to popstate (back/forward navigation)
|
|
59
|
+
window.addEventListener('popstate', updateQueryParams);
|
|
60
|
+
|
|
61
|
+
// Poll for query param changes (for router.push with same pathname)
|
|
62
|
+
// This handles cases where Next.js router.push updates query params without changing pathname
|
|
63
|
+
const intervalId = setInterval(updateQueryParams, 100);
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
window.removeEventListener('popstate', updateQueryParams);
|
|
67
|
+
clearInterval(intervalId);
|
|
68
|
+
};
|
|
69
|
+
}, [pathname]);
|
|
70
|
+
|
|
71
|
+
return queryParams;
|
|
72
|
+
}
|
|
73
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useState } from 'react';
|
|
4
4
|
|
|
5
|
-
import { useCfgRouter } from '
|
|
5
|
+
import { useCfgRouter } from './useCfgRouter';
|
|
6
6
|
|
|
7
7
|
import { apiAccounts, apiTotp } from '../../clients';
|
|
8
8
|
import { Analytics, AnalyticsCategory, AnalyticsEvent } from '../utils/analytics';
|
|
@@ -71,7 +71,10 @@ export const useTwoFactorStatus = (): UseTwoFactorStatusReturn => {
|
|
|
71
71
|
const response = await apiTotp.totp_management.totpDevicesList();
|
|
72
72
|
|
|
73
73
|
// Map devices to our format
|
|
74
|
-
|
|
74
|
+
|
|
75
|
+
// Join all devices into a single array
|
|
76
|
+
const devices = response.results.flatMap((device) => device.devices);
|
|
77
|
+
const mappedDevices: TwoFactorDevice[] = devices.map((device) => ({
|
|
75
78
|
id: device.id,
|
|
76
79
|
name: device.name,
|
|
77
80
|
createdAt: device.created_at,
|
|
@@ -80,6 +83,7 @@ export const useTwoFactorStatus = (): UseTwoFactorStatusReturn => {
|
|
|
80
83
|
}));
|
|
81
84
|
|
|
82
85
|
setDevices(mappedDevices);
|
|
86
|
+
|
|
83
87
|
// 2FA is enabled if there are any devices
|
|
84
88
|
const enabled = mappedDevices.length > 0;
|
|
85
89
|
setHas2FAEnabled(enabled);
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import { consola } from 'consola'
|
|
34
34
|
import { DisableRequestSchema, type DisableRequest } from '../schemas/DisableRequest.schema'
|
|
35
|
-
import {
|
|
35
|
+
import { PaginatedDeviceListResponseListSchema, type PaginatedDeviceListResponseList } from '../schemas/PaginatedDeviceListResponseList.schema'
|
|
36
36
|
import { getAPIInstance } from '../../api-instance'
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -42,11 +42,11 @@ import { getAPIInstance } from '../../api-instance'
|
|
|
42
42
|
* @path /cfg/totp/devices/
|
|
43
43
|
*/
|
|
44
44
|
export async function getTotpDevicesList( params?: { page?: number; page_size?: number }, client?: any
|
|
45
|
-
): Promise<
|
|
45
|
+
): Promise<PaginatedDeviceListResponseList> {
|
|
46
46
|
const api = client || getAPIInstance()
|
|
47
47
|
const response = await api.totp_management.totpDevicesList(params?.page, params?.page_size)
|
|
48
48
|
try {
|
|
49
|
-
return
|
|
49
|
+
return PaginatedDeviceListResponseListSchema.parse(response)
|
|
50
50
|
} catch (error) {
|
|
51
51
|
// Zod validation error - log detailed information
|
|
52
52
|
consola.error('❌ Zod Validation Failed');
|
|
@@ -22,7 +22,7 @@ import { useSWRConfig } from 'swr'
|
|
|
22
22
|
import * as Fetchers from '../fetchers/totp__totp_management'
|
|
23
23
|
import type { API } from '../../index'
|
|
24
24
|
import type { DisableRequest } from '../schemas/DisableRequest.schema'
|
|
25
|
-
import type {
|
|
25
|
+
import type { PaginatedDeviceListResponseList } from '../schemas/PaginatedDeviceListResponseList.schema'
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* API operation
|
|
@@ -30,8 +30,8 @@ import type { PaginatedDeviceListList } from '../schemas/PaginatedDeviceListList
|
|
|
30
30
|
* @method GET
|
|
31
31
|
* @path /cfg/totp/devices/
|
|
32
32
|
*/
|
|
33
|
-
export function useTotpDevicesList(params?: { page?: number; page_size?: number }, client?: API): ReturnType<typeof useSWR<
|
|
34
|
-
return useSWR<
|
|
33
|
+
export function useTotpDevicesList(params?: { page?: number; page_size?: number }, client?: API): ReturnType<typeof useSWR<PaginatedDeviceListResponseList>> {
|
|
34
|
+
return useSWR<PaginatedDeviceListResponseList>(
|
|
35
35
|
params ? ['cfg-totp-devices', params] : 'cfg-totp-devices',
|
|
36
36
|
() => Fetchers.getTotpDevicesList(params, client)
|
|
37
37
|
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema for DeviceListResponse
|
|
3
|
+
*
|
|
4
|
+
* This schema provides runtime validation and type inference.
|
|
5
|
+
* * Response serializer for device list endpoint.
|
|
6
|
+
* */
|
|
7
|
+
import { z } from 'zod'
|
|
8
|
+
import { DeviceListSchema } from './DeviceList.schema'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Response serializer for device list endpoint.
|
|
12
|
+
*/
|
|
13
|
+
export const DeviceListResponseSchema = z.object({
|
|
14
|
+
devices: z.array(DeviceListSchema),
|
|
15
|
+
has_2fa_enabled: z.boolean(),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Infer TypeScript type from Zod schema
|
|
20
|
+
*/
|
|
21
|
+
export type DeviceListResponse = z.infer<typeof DeviceListResponseSchema>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Zod schema for
|
|
2
|
+
* Zod schema for PaginatedDeviceListResponseList
|
|
3
3
|
*
|
|
4
4
|
* This schema provides runtime validation and type inference.
|
|
5
5
|
* */
|
|
6
6
|
import { z } from 'zod'
|
|
7
|
-
import {
|
|
7
|
+
import { DeviceListResponseSchema } from './DeviceListResponse.schema'
|
|
8
8
|
|
|
9
|
-
export const
|
|
9
|
+
export const PaginatedDeviceListResponseListSchema = z.object({
|
|
10
10
|
count: z.int(),
|
|
11
11
|
page: z.int(),
|
|
12
12
|
pages: z.int(),
|
|
@@ -15,10 +15,10 @@ export const PaginatedDeviceListListSchema = z.object({
|
|
|
15
15
|
has_previous: z.boolean(),
|
|
16
16
|
next_page: z.int().nullable().optional(),
|
|
17
17
|
previous_page: z.int().nullable().optional(),
|
|
18
|
-
results: z.array(
|
|
18
|
+
results: z.array(DeviceListResponseSchema),
|
|
19
19
|
})
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Infer TypeScript type from Zod schema
|
|
23
23
|
*/
|
|
24
|
-
export type
|
|
24
|
+
export type PaginatedDeviceListResponseList = z.infer<typeof PaginatedDeviceListResponseListSchema>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema for TotpVerifyUser
|
|
3
|
+
*
|
|
4
|
+
* This schema provides runtime validation and type inference.
|
|
5
|
+
* * User data returned after 2FA verification.
|
|
6
|
+
* */
|
|
7
|
+
import { z } from 'zod'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* User data returned after 2FA verification.
|
|
11
|
+
*/
|
|
12
|
+
export const TotpVerifyUserSchema = z.object({
|
|
13
|
+
id: z.int(),
|
|
14
|
+
email: z.email(),
|
|
15
|
+
first_name: z.string().max(50).optional(),
|
|
16
|
+
last_name: z.string().max(50).optional(),
|
|
17
|
+
full_name: z.string(),
|
|
18
|
+
initials: z.string(),
|
|
19
|
+
display_username: z.string(),
|
|
20
|
+
company: z.string().max(100).optional(),
|
|
21
|
+
phone: z.string().max(20).optional(),
|
|
22
|
+
position: z.string().max(100).optional(),
|
|
23
|
+
avatar: z.union([z.url(), z.literal('')]).nullable(),
|
|
24
|
+
is_staff: z.boolean(),
|
|
25
|
+
is_superuser: z.boolean(),
|
|
26
|
+
date_joined: z.iso.datetime(),
|
|
27
|
+
last_login: z.iso.datetime().nullable(),
|
|
28
|
+
unanswered_messages_count: z.int(),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Infer TypeScript type from Zod schema
|
|
33
|
+
*/
|
|
34
|
+
export type TotpVerifyUser = z.infer<typeof TotpVerifyUserSchema>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* * Response serializer for successful 2FA verification.
|
|
6
6
|
* */
|
|
7
7
|
import { z } from 'zod'
|
|
8
|
+
import { TotpVerifyUserSchema } from './TotpVerifyUser.schema'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Response serializer for successful 2FA verification.
|
|
@@ -13,7 +14,7 @@ export const VerifyResponseSchema = z.object({
|
|
|
13
14
|
message: z.string(),
|
|
14
15
|
access_token: z.string(),
|
|
15
16
|
refresh_token: z.string(),
|
|
16
|
-
user:
|
|
17
|
+
user: TotpVerifyUserSchema,
|
|
17
18
|
remaining_backup_codes: z.int().optional(),
|
|
18
19
|
warning: z.string().optional(),
|
|
19
20
|
})
|
|
@@ -23,10 +23,12 @@ export * from './BackupCodesStatus.schema'
|
|
|
23
23
|
export * from './ConfirmSetupRequest.schema'
|
|
24
24
|
export * from './ConfirmSetupResponse.schema'
|
|
25
25
|
export * from './DeviceList.schema'
|
|
26
|
+
export * from './DeviceListResponse.schema'
|
|
26
27
|
export * from './DisableRequest.schema'
|
|
27
|
-
export * from './
|
|
28
|
+
export * from './PaginatedDeviceListResponseList.schema'
|
|
28
29
|
export * from './SetupRequest.schema'
|
|
29
30
|
export * from './SetupResponse.schema'
|
|
31
|
+
export * from './TotpVerifyUser.schema'
|
|
30
32
|
export * from './VerifyBackupRequest.schema'
|
|
31
33
|
export * from './VerifyRequest.schema'
|
|
32
34
|
export * from './VerifyResponse.schema'
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
"content": {
|
|
136
136
|
"application/json": {
|
|
137
137
|
"schema": {
|
|
138
|
-
"$ref": "#/components/schemas/
|
|
138
|
+
"$ref": "#/components/schemas/PaginatedDeviceListResponseList"
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
},
|
|
@@ -640,6 +640,25 @@
|
|
|
640
640
|
"status"
|
|
641
641
|
]
|
|
642
642
|
},
|
|
643
|
+
"DeviceListResponse": {
|
|
644
|
+
"type": "object",
|
|
645
|
+
"description": "Response serializer for device list endpoint.",
|
|
646
|
+
"properties": {
|
|
647
|
+
"devices": {
|
|
648
|
+
"type": "array",
|
|
649
|
+
"items": {
|
|
650
|
+
"$ref": "#/components/schemas/DeviceList"
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
"has_2fa_enabled": {
|
|
654
|
+
"type": "boolean"
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
"required": [
|
|
658
|
+
"devices",
|
|
659
|
+
"has_2fa_enabled"
|
|
660
|
+
]
|
|
661
|
+
},
|
|
643
662
|
"DisableRequest": {
|
|
644
663
|
"type": "object",
|
|
645
664
|
"description": "Serializer for completely disabling 2FA.",
|
|
@@ -655,7 +674,7 @@
|
|
|
655
674
|
"code"
|
|
656
675
|
]
|
|
657
676
|
},
|
|
658
|
-
"
|
|
677
|
+
"PaginatedDeviceListResponseList": {
|
|
659
678
|
"type": "object",
|
|
660
679
|
"required": [
|
|
661
680
|
"count",
|
|
@@ -712,7 +731,7 @@
|
|
|
712
731
|
"results": {
|
|
713
732
|
"type": "array",
|
|
714
733
|
"items": {
|
|
715
|
-
"$ref": "#/components/schemas/
|
|
734
|
+
"$ref": "#/components/schemas/DeviceListResponse"
|
|
716
735
|
},
|
|
717
736
|
"description": "Array of items for current page"
|
|
718
737
|
}
|
|
@@ -765,6 +784,103 @@
|
|
|
765
784
|
"secret"
|
|
766
785
|
]
|
|
767
786
|
},
|
|
787
|
+
"TotpVerifyUser": {
|
|
788
|
+
"type": "object",
|
|
789
|
+
"description": "User data returned after 2FA verification.",
|
|
790
|
+
"properties": {
|
|
791
|
+
"id": {
|
|
792
|
+
"type": "integer",
|
|
793
|
+
"readOnly": true
|
|
794
|
+
},
|
|
795
|
+
"email": {
|
|
796
|
+
"type": "string",
|
|
797
|
+
"format": "email",
|
|
798
|
+
"readOnly": true
|
|
799
|
+
},
|
|
800
|
+
"first_name": {
|
|
801
|
+
"type": "string",
|
|
802
|
+
"maxLength": 50
|
|
803
|
+
},
|
|
804
|
+
"last_name": {
|
|
805
|
+
"type": "string",
|
|
806
|
+
"maxLength": 50
|
|
807
|
+
},
|
|
808
|
+
"full_name": {
|
|
809
|
+
"type": "string",
|
|
810
|
+
"description": "Get user's full name.",
|
|
811
|
+
"readOnly": true
|
|
812
|
+
},
|
|
813
|
+
"initials": {
|
|
814
|
+
"type": "string",
|
|
815
|
+
"description": "Get user's initials for avatar fallback.",
|
|
816
|
+
"readOnly": true
|
|
817
|
+
},
|
|
818
|
+
"display_username": {
|
|
819
|
+
"type": "string",
|
|
820
|
+
"description": "Get formatted username for display.",
|
|
821
|
+
"readOnly": true
|
|
822
|
+
},
|
|
823
|
+
"company": {
|
|
824
|
+
"type": "string",
|
|
825
|
+
"maxLength": 100
|
|
826
|
+
},
|
|
827
|
+
"phone": {
|
|
828
|
+
"type": "string",
|
|
829
|
+
"maxLength": 20
|
|
830
|
+
},
|
|
831
|
+
"position": {
|
|
832
|
+
"type": "string",
|
|
833
|
+
"maxLength": 100
|
|
834
|
+
},
|
|
835
|
+
"avatar": {
|
|
836
|
+
"type": "string",
|
|
837
|
+
"format": "uri",
|
|
838
|
+
"nullable": true,
|
|
839
|
+
"readOnly": true
|
|
840
|
+
},
|
|
841
|
+
"is_staff": {
|
|
842
|
+
"type": "boolean",
|
|
843
|
+
"readOnly": true,
|
|
844
|
+
"title": "Staff status",
|
|
845
|
+
"description": "Designates whether the user can log into this admin site."
|
|
846
|
+
},
|
|
847
|
+
"is_superuser": {
|
|
848
|
+
"type": "boolean",
|
|
849
|
+
"readOnly": true,
|
|
850
|
+
"title": "Superuser status",
|
|
851
|
+
"description": "Designates that this user has all permissions without explicitly assigning them."
|
|
852
|
+
},
|
|
853
|
+
"date_joined": {
|
|
854
|
+
"type": "string",
|
|
855
|
+
"format": "date-time",
|
|
856
|
+
"readOnly": true
|
|
857
|
+
},
|
|
858
|
+
"last_login": {
|
|
859
|
+
"type": "string",
|
|
860
|
+
"format": "date-time",
|
|
861
|
+
"readOnly": true,
|
|
862
|
+
"nullable": true
|
|
863
|
+
},
|
|
864
|
+
"unanswered_messages_count": {
|
|
865
|
+
"type": "integer",
|
|
866
|
+
"readOnly": true,
|
|
867
|
+
"default": 0
|
|
868
|
+
}
|
|
869
|
+
},
|
|
870
|
+
"required": [
|
|
871
|
+
"avatar",
|
|
872
|
+
"date_joined",
|
|
873
|
+
"display_username",
|
|
874
|
+
"email",
|
|
875
|
+
"full_name",
|
|
876
|
+
"id",
|
|
877
|
+
"initials",
|
|
878
|
+
"is_staff",
|
|
879
|
+
"is_superuser",
|
|
880
|
+
"last_login",
|
|
881
|
+
"unanswered_messages_count"
|
|
882
|
+
]
|
|
883
|
+
},
|
|
768
884
|
"VerifyBackupRequest": {
|
|
769
885
|
"type": "object",
|
|
770
886
|
"description": "Serializer for backup code verification during login.",
|
|
@@ -823,8 +939,11 @@
|
|
|
823
939
|
"description": "JWT refresh token"
|
|
824
940
|
},
|
|
825
941
|
"user": {
|
|
826
|
-
"
|
|
827
|
-
|
|
942
|
+
"allOf": [
|
|
943
|
+
{
|
|
944
|
+
"$ref": "#/components/schemas/TotpVerifyUser"
|
|
945
|
+
}
|
|
946
|
+
],
|
|
828
947
|
"description": "User profile data"
|
|
829
948
|
},
|
|
830
949
|
"remaining_backup_codes": {
|