@djangocfg/layouts 2.1.10 → 2.1.15
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.md +53 -161
- package/package.json +6 -6
- package/src/components/RedirectPage/RedirectPage.tsx +1 -1
- package/src/index.ts +0 -6
- package/src/layouts/AppLayout/AppLayout.tsx +1 -1
- package/src/layouts/AppLayout/BaseApp.tsx +1 -1
- package/src/layouts/AuthLayout/AuthContext.tsx +1 -1
- package/src/layouts/AuthLayout/OAuthCallback.tsx +1 -1
- package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -1
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +1 -1
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +2 -2
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +2 -2
- package/src/layouts/ProfileLayout/components/ProfileForm.tsx +2 -2
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
- package/src/layouts/_components/UserMenu.tsx +1 -1
- package/src/layouts/index.ts +0 -2
- package/src/snippets/Analytics/useAnalytics.ts +1 -1
- package/src/snippets/index.ts +0 -3
- package/src/auth/README.md +0 -962
- package/src/auth/context/AccountsContext.tsx +0 -240
- package/src/auth/context/AuthContext.tsx +0 -604
- package/src/auth/context/index.ts +0 -4
- package/src/auth/context/types.ts +0 -68
- package/src/auth/hooks/index.ts +0 -17
- package/src/auth/hooks/useAuthForm.ts +0 -332
- package/src/auth/hooks/useAuthGuard.ts +0 -25
- package/src/auth/hooks/useAuthRedirect.ts +0 -51
- package/src/auth/hooks/useAutoAuth.ts +0 -49
- package/src/auth/hooks/useGithubAuth.ts +0 -184
- package/src/auth/hooks/useLocalStorage.ts +0 -214
- package/src/auth/hooks/useProfileCache.ts +0 -146
- package/src/auth/hooks/useSessionStorage.ts +0 -189
- package/src/auth/index.ts +0 -10
- package/src/auth/middlewares/index.ts +0 -1
- package/src/auth/middlewares/proxy.ts +0 -32
- package/src/auth/server.ts +0 -6
- package/src/auth/utils/errors.ts +0 -34
- package/src/auth/utils/index.ts +0 -2
- package/src/auth/utils/validation.ts +0 -14
- package/src/contexts/LeadsContext.tsx +0 -156
- package/src/contexts/NewsletterContext.tsx +0 -263
- package/src/contexts/SupportContext.tsx +0 -256
- package/src/contexts/index.ts +0 -59
- package/src/contexts/knowbase/ChatContext.tsx +0 -174
- package/src/contexts/knowbase/DocumentsContext.tsx +0 -304
- package/src/contexts/knowbase/SessionsContext.tsx +0 -174
- package/src/contexts/knowbase/index.ts +0 -61
- package/src/contexts/payments/BalancesContext.tsx +0 -65
- package/src/contexts/payments/CurrenciesContext.tsx +0 -66
- package/src/contexts/payments/OverviewContext.tsx +0 -174
- package/src/contexts/payments/PaymentsContext.tsx +0 -132
- package/src/contexts/payments/README.md +0 -201
- package/src/contexts/payments/RootPaymentsContext.tsx +0 -68
- package/src/contexts/payments/index.ts +0 -50
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -92
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -291
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -290
- package/src/layouts/PaymentsLayout/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/events.ts +0 -47
- package/src/layouts/PaymentsLayout/index.ts +0 -16
- package/src/layouts/PaymentsLayout/types.ts +0 -6
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -128
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -142
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -20
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -276
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -17
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -273
- package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -17
- package/src/layouts/SupportLayout/README.md +0 -91
- package/src/layouts/SupportLayout/SupportLayout.tsx +0 -179
- package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +0 -155
- package/src/layouts/SupportLayout/components/MessageInput.tsx +0 -92
- package/src/layouts/SupportLayout/components/MessageList.tsx +0 -314
- package/src/layouts/SupportLayout/components/TicketCard.tsx +0 -96
- package/src/layouts/SupportLayout/components/TicketList.tsx +0 -153
- package/src/layouts/SupportLayout/components/index.ts +0 -6
- package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +0 -263
- package/src/layouts/SupportLayout/context/index.ts +0 -2
- package/src/layouts/SupportLayout/events.ts +0 -33
- package/src/layouts/SupportLayout/hooks/index.ts +0 -2
- package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +0 -119
- package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +0 -92
- package/src/layouts/SupportLayout/index.ts +0 -8
- package/src/layouts/SupportLayout/types.ts +0 -21
- package/src/snippets/Chat/ChatUIContext.tsx +0 -110
- package/src/snippets/Chat/ChatWidget.tsx +0 -476
- package/src/snippets/Chat/README.md +0 -122
- package/src/snippets/Chat/components/MessageInput.tsx +0 -124
- package/src/snippets/Chat/components/MessageList.tsx +0 -169
- package/src/snippets/Chat/components/SessionList.tsx +0 -192
- package/src/snippets/Chat/components/index.ts +0 -9
- package/src/snippets/Chat/hooks/index.ts +0 -6
- package/src/snippets/Chat/hooks/useInfiniteSessions.ts +0 -82
- package/src/snippets/Chat/index.tsx +0 -45
- package/src/snippets/Chat/types.ts +0 -80
- package/src/snippets/ContactForm/ContactForm.tsx +0 -346
- package/src/snippets/ContactForm/ContactFormProvider.tsx +0 -153
- package/src/snippets/ContactForm/ContactInfo.tsx +0 -114
- package/src/snippets/ContactForm/ContactPage.tsx +0 -131
- package/src/snippets/ContactForm/dynamic.tsx +0 -55
- package/src/snippets/ContactForm/index.ts +0 -34
- package/src/snippets/ContactForm/types.ts +0 -110
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
import { authLogger } from '../../utils/logger';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Simple localStorage hook with better error handling
|
|
8
|
-
* @param key - Storage key
|
|
9
|
-
* @param initialValue - Default value if key doesn't exist
|
|
10
|
-
* @returns [value, setValue, removeValue] - Current value, setter function, and remove function
|
|
11
|
-
*/
|
|
12
|
-
export function useLocalStorage<T>(key: string, initialValue: T) {
|
|
13
|
-
// Get initial value from localStorage or use provided initialValue
|
|
14
|
-
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
15
|
-
if (typeof window === 'undefined') {
|
|
16
|
-
return initialValue;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const item = window.localStorage.getItem(key);
|
|
21
|
-
if (item === null) {
|
|
22
|
-
return initialValue;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Try to parse as JSON first, fallback to string
|
|
26
|
-
try {
|
|
27
|
-
return JSON.parse(item);
|
|
28
|
-
} catch {
|
|
29
|
-
// If JSON.parse fails, return as string
|
|
30
|
-
return item as T;
|
|
31
|
-
}
|
|
32
|
-
} catch (error) {
|
|
33
|
-
authLogger.error(`Error reading localStorage key "${key}":`, error);
|
|
34
|
-
return initialValue;
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Check data size and limit
|
|
39
|
-
const checkDataSize = (data: any): boolean => {
|
|
40
|
-
try {
|
|
41
|
-
const jsonString = JSON.stringify(data);
|
|
42
|
-
const sizeInBytes = new Blob([jsonString]).size;
|
|
43
|
-
const sizeInKB = sizeInBytes / 1024;
|
|
44
|
-
|
|
45
|
-
// Limit to 1MB per item
|
|
46
|
-
if (sizeInKB > 1024) {
|
|
47
|
-
authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return true;
|
|
52
|
-
} catch (error) {
|
|
53
|
-
authLogger.error(`Error checking data size for key "${key}":`, error);
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// Clear old data when localStorage is full
|
|
59
|
-
const clearOldData = () => {
|
|
60
|
-
try {
|
|
61
|
-
const keys = Object.keys(localStorage).filter(key => key && typeof key === 'string');
|
|
62
|
-
// Remove oldest items if we have more than 50 items
|
|
63
|
-
if (keys.length > 50) {
|
|
64
|
-
const itemsToRemove = Math.ceil(keys.length * 0.2);
|
|
65
|
-
for (let i = 0; i < itemsToRemove; i++) {
|
|
66
|
-
try {
|
|
67
|
-
const key = keys[i];
|
|
68
|
-
if (key) {
|
|
69
|
-
localStorage.removeItem(key);
|
|
70
|
-
localStorage.removeItem(`${key}_timestamp`);
|
|
71
|
-
}
|
|
72
|
-
} catch {
|
|
73
|
-
// Ignore errors when removing items
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
} catch (error) {
|
|
78
|
-
authLogger.error('Error clearing old localStorage data:', error);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// Force clear all data if quota is exceeded
|
|
83
|
-
const forceClearAll = () => {
|
|
84
|
-
try {
|
|
85
|
-
const keys = Object.keys(localStorage);
|
|
86
|
-
for (const key of keys) {
|
|
87
|
-
try {
|
|
88
|
-
localStorage.removeItem(key);
|
|
89
|
-
} catch {
|
|
90
|
-
// Ignore errors when removing items
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
} catch (error) {
|
|
94
|
-
authLogger.error('Error force clearing localStorage:', error);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// Update localStorage when value changes
|
|
99
|
-
const setValue = (value: T | ((val: T) => T)) => {
|
|
100
|
-
try {
|
|
101
|
-
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
102
|
-
|
|
103
|
-
// Check data size before attempting to save
|
|
104
|
-
if (!checkDataSize(valueToStore)) {
|
|
105
|
-
authLogger.warn(`Data size too large for key "${key}", removing key`);
|
|
106
|
-
// Remove the key if data is too large
|
|
107
|
-
try {
|
|
108
|
-
window.localStorage.removeItem(key);
|
|
109
|
-
window.localStorage.removeItem(`${key}_timestamp`);
|
|
110
|
-
} catch {
|
|
111
|
-
// Ignore errors when removing
|
|
112
|
-
}
|
|
113
|
-
// Still update the state
|
|
114
|
-
setStoredValue(valueToStore);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
setStoredValue(valueToStore);
|
|
119
|
-
|
|
120
|
-
if (typeof window !== 'undefined') {
|
|
121
|
-
// Try to set the value
|
|
122
|
-
try {
|
|
123
|
-
// For strings, store directly without JSON.stringify
|
|
124
|
-
if (typeof valueToStore === 'string') {
|
|
125
|
-
window.localStorage.setItem(key, valueToStore);
|
|
126
|
-
} else {
|
|
127
|
-
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
128
|
-
}
|
|
129
|
-
// Add timestamp for cleanup
|
|
130
|
-
window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
131
|
-
} catch (storageError: any) {
|
|
132
|
-
// If quota exceeded, clear old data and try again
|
|
133
|
-
if (storageError.name === 'QuotaExceededError' ||
|
|
134
|
-
storageError.code === 22 ||
|
|
135
|
-
storageError.message?.includes('quota')) {
|
|
136
|
-
authLogger.warn('localStorage quota exceeded, clearing old data...');
|
|
137
|
-
clearOldData();
|
|
138
|
-
|
|
139
|
-
// Try again after clearing
|
|
140
|
-
try {
|
|
141
|
-
// For strings, store directly without JSON.stringify
|
|
142
|
-
if (typeof valueToStore === 'string') {
|
|
143
|
-
window.localStorage.setItem(key, valueToStore);
|
|
144
|
-
} else {
|
|
145
|
-
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
146
|
-
}
|
|
147
|
-
window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
148
|
-
} catch (retryError) {
|
|
149
|
-
authLogger.error(`Failed to set localStorage key "${key}" after clearing old data:`, retryError);
|
|
150
|
-
// If still fails, force clear all and try one more time
|
|
151
|
-
try {
|
|
152
|
-
forceClearAll();
|
|
153
|
-
// For strings, store directly without JSON.stringify
|
|
154
|
-
if (typeof valueToStore === 'string') {
|
|
155
|
-
window.localStorage.setItem(key, valueToStore);
|
|
156
|
-
} else {
|
|
157
|
-
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
158
|
-
}
|
|
159
|
-
window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
160
|
-
} catch (finalError) {
|
|
161
|
-
authLogger.error(`Failed to set localStorage key "${key}" after force clearing:`, finalError);
|
|
162
|
-
// If still fails, just update the state without localStorage
|
|
163
|
-
setStoredValue(valueToStore);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
} else {
|
|
167
|
-
throw storageError;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
} catch (error) {
|
|
172
|
-
authLogger.error(`Error setting localStorage key "${key}":`, error);
|
|
173
|
-
// Still update the state even if localStorage fails
|
|
174
|
-
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
175
|
-
setStoredValue(valueToStore);
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// Remove value from localStorage
|
|
180
|
-
const removeValue = () => {
|
|
181
|
-
try {
|
|
182
|
-
setStoredValue(initialValue);
|
|
183
|
-
if (typeof window !== 'undefined') {
|
|
184
|
-
try {
|
|
185
|
-
window.localStorage.removeItem(key);
|
|
186
|
-
window.localStorage.removeItem(`${key}_timestamp`);
|
|
187
|
-
} catch (removeError: any) {
|
|
188
|
-
// If removal fails due to quota, try to clear some data first
|
|
189
|
-
if (removeError.name === 'QuotaExceededError' ||
|
|
190
|
-
removeError.code === 22 ||
|
|
191
|
-
removeError.message?.includes('quota')) {
|
|
192
|
-
authLogger.warn('localStorage quota exceeded during removal, clearing old data...');
|
|
193
|
-
clearOldData();
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
window.localStorage.removeItem(key);
|
|
197
|
-
window.localStorage.removeItem(`${key}_timestamp`);
|
|
198
|
-
} catch (retryError) {
|
|
199
|
-
authLogger.error(`Failed to remove localStorage key "${key}" after clearing:`, retryError);
|
|
200
|
-
// If still fails, force clear all
|
|
201
|
-
forceClearAll();
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
throw removeError;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
} catch (error) {
|
|
209
|
-
authLogger.error(`Error removing localStorage key "${key}":`, error);
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
return [storedValue, setValue, removeValue] as const;
|
|
214
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Profile Cache Hook
|
|
5
|
-
*
|
|
6
|
-
* Provides localStorage-based caching for user profile with:
|
|
7
|
-
* - TTL (Time To Live) - default 1 hour
|
|
8
|
-
* - Version control for migrations
|
|
9
|
-
* - Automatic invalidation on expiry
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { User } from '@djangocfg/api';
|
|
13
|
-
import { profileLogger } from '../../utils/logger';
|
|
14
|
-
|
|
15
|
-
// Cache configuration
|
|
16
|
-
const CACHE_KEY = 'user_profile_cache';
|
|
17
|
-
const CACHE_VERSION = 1;
|
|
18
|
-
const DEFAULT_TTL = 3600000; // 1 hour in milliseconds
|
|
19
|
-
|
|
20
|
-
export interface ProfileCacheOptions {
|
|
21
|
-
/** Time to live in milliseconds (default: 1 hour) */
|
|
22
|
-
ttl?: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface CachedProfile {
|
|
26
|
-
version: number;
|
|
27
|
-
data: User;
|
|
28
|
-
timestamp: number;
|
|
29
|
-
ttl: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Get cached profile from localStorage
|
|
34
|
-
* @returns User profile if valid cache exists, null otherwise
|
|
35
|
-
*/
|
|
36
|
-
export function getCachedProfile(): User | null {
|
|
37
|
-
try {
|
|
38
|
-
if (typeof window === 'undefined') return null;
|
|
39
|
-
|
|
40
|
-
const cached = localStorage.getItem(CACHE_KEY);
|
|
41
|
-
if (!cached) {
|
|
42
|
-
profileLogger.debug('No cached profile found');
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const cachedData: CachedProfile = JSON.parse(cached);
|
|
47
|
-
|
|
48
|
-
// Version check
|
|
49
|
-
if (cachedData.version !== CACHE_VERSION) {
|
|
50
|
-
profileLogger.warn('Cache version mismatch, clearing cache');
|
|
51
|
-
clearProfileCache();
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// TTL check
|
|
56
|
-
const now = Date.now();
|
|
57
|
-
const age = now - cachedData.timestamp;
|
|
58
|
-
if (age > cachedData.ttl) {
|
|
59
|
-
profileLogger.info('Cache expired, clearing');
|
|
60
|
-
clearProfileCache();
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
profileLogger.debug('Cache hit, age:', Math.round(age / 1000), 'seconds');
|
|
65
|
-
return cachedData.data;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
profileLogger.error('Error reading cache:', error);
|
|
68
|
-
clearProfileCache();
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Save profile to localStorage cache
|
|
75
|
-
* @param profile - User profile to cache
|
|
76
|
-
* @param options - Cache options (TTL)
|
|
77
|
-
*/
|
|
78
|
-
export function setCachedProfile(profile: User, options?: ProfileCacheOptions): void {
|
|
79
|
-
try {
|
|
80
|
-
if (typeof window === 'undefined') return;
|
|
81
|
-
|
|
82
|
-
const cachedData: CachedProfile = {
|
|
83
|
-
version: CACHE_VERSION,
|
|
84
|
-
data: profile,
|
|
85
|
-
timestamp: Date.now(),
|
|
86
|
-
ttl: options?.ttl || DEFAULT_TTL,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
localStorage.setItem(CACHE_KEY, JSON.stringify(cachedData));
|
|
90
|
-
profileLogger.debug('Profile cached, TTL:', cachedData.ttl / 1000, 'seconds');
|
|
91
|
-
} catch (error) {
|
|
92
|
-
profileLogger.error('Error writing cache:', error);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Clear profile cache from localStorage
|
|
98
|
-
*/
|
|
99
|
-
export function clearProfileCache(): void {
|
|
100
|
-
try {
|
|
101
|
-
if (typeof window === 'undefined') return;
|
|
102
|
-
localStorage.removeItem(CACHE_KEY);
|
|
103
|
-
profileLogger.debug('Cache cleared');
|
|
104
|
-
} catch (error) {
|
|
105
|
-
profileLogger.error('Error clearing cache:', error);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Check if cached profile is valid (exists and not expired)
|
|
111
|
-
*/
|
|
112
|
-
export function hasValidCache(): boolean {
|
|
113
|
-
return getCachedProfile() !== null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Get cache metadata (age, TTL, etc.)
|
|
118
|
-
*/
|
|
119
|
-
export function getCacheMetadata(): {
|
|
120
|
-
exists: boolean;
|
|
121
|
-
age?: number;
|
|
122
|
-
ttl?: number;
|
|
123
|
-
expiresIn?: number;
|
|
124
|
-
} | null {
|
|
125
|
-
try {
|
|
126
|
-
if (typeof window === 'undefined') return null;
|
|
127
|
-
|
|
128
|
-
const cached = localStorage.getItem(CACHE_KEY);
|
|
129
|
-
if (!cached) return { exists: false };
|
|
130
|
-
|
|
131
|
-
const cachedData: CachedProfile = JSON.parse(cached);
|
|
132
|
-
const now = Date.now();
|
|
133
|
-
const age = now - cachedData.timestamp;
|
|
134
|
-
const expiresIn = cachedData.ttl - age;
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
exists: true,
|
|
138
|
-
age,
|
|
139
|
-
ttl: cachedData.ttl,
|
|
140
|
-
expiresIn: Math.max(0, expiresIn),
|
|
141
|
-
};
|
|
142
|
-
} catch (error) {
|
|
143
|
-
profileLogger.error('Error reading metadata:', error);
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { authLogger } from '../../utils/logger';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Simple sessionStorage hook with better error handling
|
|
8
|
-
* @param key - Storage key
|
|
9
|
-
* @param initialValue - Default value if key doesn't exist
|
|
10
|
-
* @returns [value, setValue, removeValue] - Current value, setter function, and remove function
|
|
11
|
-
*/
|
|
12
|
-
export function useSessionStorage<T>(key: string, initialValue: T) {
|
|
13
|
-
// Get initial value from sessionStorage or use provided initialValue
|
|
14
|
-
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
15
|
-
if (typeof window === 'undefined') {
|
|
16
|
-
return initialValue;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const item = window.sessionStorage.getItem(key);
|
|
21
|
-
return item ? JSON.parse(item) : initialValue;
|
|
22
|
-
} catch (error) {
|
|
23
|
-
authLogger.error(`Error reading sessionStorage key "${key}":`, error);
|
|
24
|
-
return initialValue;
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Check data size and limit
|
|
29
|
-
const checkDataSize = (data: any): boolean => {
|
|
30
|
-
try {
|
|
31
|
-
const jsonString = JSON.stringify(data);
|
|
32
|
-
const sizeInBytes = new Blob([jsonString]).size;
|
|
33
|
-
const sizeInKB = sizeInBytes / 1024;
|
|
34
|
-
|
|
35
|
-
// Limit to 1MB per item
|
|
36
|
-
if (sizeInKB > 1024) {
|
|
37
|
-
authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return true;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
authLogger.error(`Error checking data size for key "${key}":`, error);
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// Clear old data when sessionStorage is full
|
|
49
|
-
const clearOldData = () => {
|
|
50
|
-
try {
|
|
51
|
-
const keys = Object.keys(sessionStorage).filter(key => key && typeof key === 'string');
|
|
52
|
-
// Remove oldest items if we have more than 50 items
|
|
53
|
-
if (keys.length > 50) {
|
|
54
|
-
const itemsToRemove = Math.ceil(keys.length * 0.2);
|
|
55
|
-
for (let i = 0; i < itemsToRemove; i++) {
|
|
56
|
-
try {
|
|
57
|
-
const key = keys[i];
|
|
58
|
-
if (key) {
|
|
59
|
-
sessionStorage.removeItem(key);
|
|
60
|
-
sessionStorage.removeItem(`${key}_timestamp`);
|
|
61
|
-
}
|
|
62
|
-
} catch {
|
|
63
|
-
// Ignore errors when removing items
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
authLogger.error('Error clearing old sessionStorage data:', error);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// Force clear all data if quota is exceeded
|
|
73
|
-
const forceClearAll = () => {
|
|
74
|
-
try {
|
|
75
|
-
const keys = Object.keys(sessionStorage);
|
|
76
|
-
for (const key of keys) {
|
|
77
|
-
try {
|
|
78
|
-
sessionStorage.removeItem(key);
|
|
79
|
-
} catch {
|
|
80
|
-
// Ignore errors when removing items
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
authLogger.error('Error force clearing sessionStorage:', error);
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
// Update sessionStorage when value changes
|
|
89
|
-
const setValue = (value: T | ((val: T) => T)) => {
|
|
90
|
-
try {
|
|
91
|
-
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
92
|
-
|
|
93
|
-
// Check data size before attempting to save
|
|
94
|
-
if (!checkDataSize(valueToStore)) {
|
|
95
|
-
authLogger.warn(`Data size too large for key "${key}", removing key`);
|
|
96
|
-
// Remove the key if data is too large
|
|
97
|
-
try {
|
|
98
|
-
window.sessionStorage.removeItem(key);
|
|
99
|
-
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
100
|
-
} catch {
|
|
101
|
-
// Ignore errors when removing
|
|
102
|
-
}
|
|
103
|
-
// Still update the state
|
|
104
|
-
setStoredValue(valueToStore);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
setStoredValue(valueToStore);
|
|
109
|
-
|
|
110
|
-
if (typeof window !== 'undefined') {
|
|
111
|
-
// Try to set the value
|
|
112
|
-
try {
|
|
113
|
-
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
114
|
-
// Add timestamp for cleanup
|
|
115
|
-
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
116
|
-
} catch (storageError: any) {
|
|
117
|
-
// If quota exceeded, clear old data and try again
|
|
118
|
-
if (storageError.name === 'QuotaExceededError' ||
|
|
119
|
-
storageError.code === 22 ||
|
|
120
|
-
storageError.message?.includes('quota')) {
|
|
121
|
-
authLogger.warn('sessionStorage quota exceeded, clearing old data...');
|
|
122
|
-
clearOldData();
|
|
123
|
-
|
|
124
|
-
// Try again after clearing
|
|
125
|
-
try {
|
|
126
|
-
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
127
|
-
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
128
|
-
} catch (retryError) {
|
|
129
|
-
authLogger.error(`Failed to set sessionStorage key "${key}" after clearing old data:`, retryError);
|
|
130
|
-
// If still fails, force clear all and try one more time
|
|
131
|
-
try {
|
|
132
|
-
forceClearAll();
|
|
133
|
-
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
134
|
-
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
135
|
-
} catch (finalError) {
|
|
136
|
-
authLogger.error(`Failed to set sessionStorage key "${key}" after force clearing:`, finalError);
|
|
137
|
-
// If still fails, just update the state without sessionStorage
|
|
138
|
-
setStoredValue(valueToStore);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
throw storageError;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
} catch (error) {
|
|
147
|
-
authLogger.error(`Error setting sessionStorage key "${key}":`, error);
|
|
148
|
-
// Still update the state even if sessionStorage fails
|
|
149
|
-
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
150
|
-
setStoredValue(valueToStore);
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// Remove value from sessionStorage
|
|
155
|
-
const removeValue = () => {
|
|
156
|
-
try {
|
|
157
|
-
setStoredValue(initialValue);
|
|
158
|
-
if (typeof window !== 'undefined') {
|
|
159
|
-
try {
|
|
160
|
-
window.sessionStorage.removeItem(key);
|
|
161
|
-
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
162
|
-
} catch (removeError: any) {
|
|
163
|
-
// If removal fails due to quota, try to clear some data first
|
|
164
|
-
if (removeError.name === 'QuotaExceededError' ||
|
|
165
|
-
removeError.code === 22 ||
|
|
166
|
-
removeError.message?.includes('quota')) {
|
|
167
|
-
authLogger.warn('sessionStorage quota exceeded during removal, clearing old data...');
|
|
168
|
-
clearOldData();
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
window.sessionStorage.removeItem(key);
|
|
172
|
-
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
173
|
-
} catch (retryError) {
|
|
174
|
-
authLogger.error(`Failed to remove sessionStorage key "${key}" after clearing:`, retryError);
|
|
175
|
-
// If still fails, force clear all
|
|
176
|
-
forceClearAll();
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
throw removeError;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
} catch (error) {
|
|
184
|
-
authLogger.error(`Error removing sessionStorage key "${key}":`, error);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
return [storedValue, setValue, removeValue] as const;
|
|
189
|
-
}
|
package/src/auth/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { proxyMiddleware, proxyMiddlewareConfig } from './proxy';
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Proxy middleware for media and API endpoints
|
|
5
|
-
* Use this in your middleware.ts file
|
|
6
|
-
*/
|
|
7
|
-
export function proxyMiddleware(request: NextRequest) {
|
|
8
|
-
const { pathname, search } = request.nextUrl;
|
|
9
|
-
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
|
|
10
|
-
|
|
11
|
-
// Proxy /media/* - Images and static files
|
|
12
|
-
if (pathname.startsWith('/media/')) {
|
|
13
|
-
const targetUrl = `${apiUrl}${pathname}${search}`;
|
|
14
|
-
return NextResponse.rewrite(targetUrl, { request: { headers: request.headers } });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Proxy /api/* - API endpoints
|
|
18
|
-
if (pathname.startsWith('/api/')) {
|
|
19
|
-
const targetUrl = `${apiUrl}${pathname}${search}`;
|
|
20
|
-
return NextResponse.rewrite(targetUrl, { request: { headers: request.headers } });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return NextResponse.next();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Recommended matcher config for proxy middleware
|
|
28
|
-
* Add this to your middleware.ts config
|
|
29
|
-
*/
|
|
30
|
-
export const proxyMiddlewareConfig = {
|
|
31
|
-
matcher: ['/media/:path*', '/api/:path*'],
|
|
32
|
-
};
|
package/src/auth/server.ts
DELETED
package/src/auth/utils/errors.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Format authentication error messages
|
|
3
|
-
*/
|
|
4
|
-
export const formatAuthError = (error: any): string => {
|
|
5
|
-
if (typeof error === 'string') {
|
|
6
|
-
return error;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
if (error?.message) {
|
|
10
|
-
return error.message;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (error?.response?.data?.message) {
|
|
14
|
-
return error.response.data.message;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (error?.response?.data?.detail) {
|
|
18
|
-
return error.response.data.detail;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return 'An unexpected error occurred';
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Common error messages
|
|
26
|
-
*/
|
|
27
|
-
export const AUTH_ERRORS = {
|
|
28
|
-
INVALID_EMAIL: 'Please enter a valid email address',
|
|
29
|
-
INVALID_OTP: 'Please enter a valid 6-digit verification code',
|
|
30
|
-
NETWORK_ERROR: 'Network error. Please check your connection',
|
|
31
|
-
SERVER_ERROR: 'Server error. Please try again later',
|
|
32
|
-
UNAUTHORIZED: 'Unauthorized. Please sign in again',
|
|
33
|
-
TOKEN_EXPIRED: 'Session expired. Please sign in again',
|
|
34
|
-
} as const;
|
package/src/auth/utils/index.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Email validation utility
|
|
3
|
-
*/
|
|
4
|
-
export const validateEmail = (email: string): boolean => {
|
|
5
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6
|
-
return emailRegex.test(email);
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* OTP validation utility
|
|
11
|
-
*/
|
|
12
|
-
export const validateOTP = (otp: string): boolean => {
|
|
13
|
-
return /^\d{6}$/.test(otp);
|
|
14
|
-
};
|