@promakeai/cli 0.0.5 → 0.0.6
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/index.js +214 -135
- package/dist/registry/about-page.json +1 -1
- package/dist/registry/about-section.json +1 -1
- package/dist/registry/api.json +55 -0
- package/dist/registry/auth.json +70 -0
- package/dist/registry/bento-grid-section.json +1 -1
- package/dist/registry/blog-list-page.json +1 -1
- package/dist/registry/blog-section.json +1 -1
- package/dist/registry/cart-drawer.json +1 -1
- package/dist/registry/cart-page.json +3 -2
- package/dist/registry/category-section.json +1 -1
- package/dist/registry/checkout-page.json +3 -2
- package/dist/registry/contact-info-grid.json +1 -1
- package/dist/registry/contact-page-centered.json +1 -1
- package/dist/registry/contact-page-map-overlay.json +1 -1
- package/dist/registry/contact-page.json +1 -1
- package/dist/registry/cookies-page.json +1 -1
- package/dist/registry/cta-section.json +1 -1
- package/dist/registry/db.json +129 -0
- package/dist/registry/docs/cart-page.md +1 -0
- package/dist/registry/docs/checkout-page.md +1 -0
- package/dist/registry/docs/forgot-password-page.md +37 -0
- package/dist/registry/docs/header-ecommerce.md +1 -0
- package/dist/registry/docs/products-page.md +1 -0
- package/dist/registry/docs/register-page.md +39 -0
- package/dist/registry/ecommerce-core.json +1 -1
- package/dist/registry/empty-page.json +1 -1
- package/dist/registry/faq-categorized.json +1 -1
- package/dist/registry/faq-simple.json +1 -1
- package/dist/registry/favorites-blog-block.json +1 -1
- package/dist/registry/favorites-ecommerce-block.json +1 -1
- package/dist/registry/feature-section.json +1 -1
- package/dist/registry/featured-products.json +1 -1
- package/dist/registry/footer-detailed.json +1 -1
- package/dist/registry/footer-minimal.json +3 -3
- package/dist/registry/footer.json +1 -1
- package/dist/registry/forgot-password-page.json +49 -0
- package/dist/registry/header-ecommerce.json +3 -2
- package/dist/registry/header-mega.json +1 -1
- package/dist/registry/header-minimal.json +1 -1
- package/dist/registry/header-simple.json +1 -1
- package/dist/registry/hero-cta.json +1 -1
- package/dist/registry/hero-gradient.json +1 -1
- package/dist/registry/hero-profile.json +1 -1
- package/dist/registry/hero.json +1 -1
- package/dist/registry/index.json +3 -0
- package/dist/registry/orders-list-block.json +1 -1
- package/dist/registry/payment-success-block.json +1 -1
- package/dist/registry/post-detail-block.json +1 -1
- package/dist/registry/pricing-section.json +1 -1
- package/dist/registry/privacy-page.json +1 -1
- package/dist/registry/products-page.json +3 -2
- package/dist/registry/register-page.json +49 -0
- package/dist/registry/related-posts-block.json +1 -1
- package/dist/registry/terms-page.json +1 -1
- package/dist/registry/testimonials-carousel.json +1 -1
- package/dist/registry/testimonials-grid.json +1 -1
- package/package.json +1 -1
- package/template/src/App.tsx +3 -24
- package/template/src/components/Layout.tsx +0 -4
- package/template/src/index.css +1 -0
- package/template/src/lang/en/index.json +1 -28
- package/template/src/lang/tr/index.json +1 -28
- package/template/src/pages/Index.tsx +1 -102
- package/template/src/components/Footer.tsx +0 -100
- package/template/src/components/Header.tsx +0 -79
- package/template/src/components/Hero.tsx +0 -69
- package/template/src/modules/api/USAGE.md +0 -515
- package/template/src/modules/api/customer-client.ts +0 -20
- package/template/src/modules/api/get-error-message.ts +0 -18
- package/template/src/modules/api/validation/en.json +0 -29
- package/template/src/modules/api/validation/tr.json +0 -29
- package/template/src/modules/auth/USAGE.md +0 -248
- package/template/src/modules/auth/auth-header-menu.tsx +0 -123
- package/template/src/modules/auth/auth-store.ts +0 -57
- package/template/src/modules/auth/forgot-password-page.tsx +0 -371
- package/template/src/modules/auth/login-page.tsx +0 -183
- package/template/src/modules/auth/register-page.tsx +0 -252
- package/template/src/modules/auth/use-auth.ts +0 -273
- package/template/src/modules/db/adapters/IDataAdapter.ts +0 -26
- package/template/src/modules/db/adapters/SqliteAdapter.ts +0 -364
- package/template/src/modules/db/adapters/index.ts +0 -2
- package/template/src/modules/db/config.ts +0 -59
- package/template/src/modules/db/core/DataManager.ts +0 -125
- package/template/src/modules/db/core/types.ts +0 -101
- package/template/src/modules/db/index.ts +0 -42
- package/template/src/modules/db/react/QueryProvider.tsx +0 -16
- package/template/src/modules/db/react/index.ts +0 -23
- package/template/src/modules/db/react/queryClient.ts +0 -64
- package/template/src/modules/db/react/useRepository.ts +0 -400
- package/template/src/modules/db/utils/parsers.ts +0 -96
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
# Auth Module Usage Guide
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Auth module manages user authentication with Zustand state, automatic token refresh, and axios interceptors. Provides secure authentication integrated with the customer backend client.
|
|
6
|
-
|
|
7
|
-
**Important**: UI components (pages, forms) are customizable, but the core auth logic (`auth-store.ts`, `use-auth.ts`) and API integration should remain unchanged to ensure proper token management and security.
|
|
8
|
-
|
|
9
|
-
## Module Structure
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
src/modules/auth/
|
|
13
|
-
├── auth-store.ts # [CORE] Zustand store - DO NOT MODIFY
|
|
14
|
-
├── use-auth.ts # [CORE] Auth hook with token refresh - DO NOT MODIFY
|
|
15
|
-
├── login-page.tsx # [UI] Customizable
|
|
16
|
-
├── register-page.tsx # [UI] Customizable
|
|
17
|
-
├── forgot-password-page.tsx # [UI] Customizable
|
|
18
|
-
└── auth-header-menu.tsx # [UI] Customizable
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Requirements & Dependencies
|
|
22
|
-
|
|
23
|
-
### Required NPM Packages
|
|
24
|
-
|
|
25
|
-
```json
|
|
26
|
-
{
|
|
27
|
-
"zustand": "^5.0.9", // State management (CORE)
|
|
28
|
-
"react-router-dom": "^6.30.1", // Navigation (UI)
|
|
29
|
-
"sonner": "^1.7.4", // Toast notifications (UI)
|
|
30
|
-
"lucide-react": "^0.462.0" // Icons (UI)
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Required Project Modules
|
|
35
|
-
|
|
36
|
-
- **`@/modules/api/customer-client`** - API client instance (CORE)
|
|
37
|
-
- **`@/modules/api/get-error-message`** - Error handling utility (CORE)
|
|
38
|
-
|
|
39
|
-
### Required Project Files
|
|
40
|
-
|
|
41
|
-
**Hooks** (CORE - Must exist):
|
|
42
|
-
|
|
43
|
-
- `@/hooks/useStaticText` - Translation system
|
|
44
|
-
- `@/hooks/usePageTitle` - Page title management
|
|
45
|
-
- `@/hooks/useSiteConfig` - Site configuration (used by useStaticText)
|
|
46
|
-
|
|
47
|
-
**Components** (UI - Customizable):
|
|
48
|
-
|
|
49
|
-
- `@/components/Layout` - Page layout wrapper
|
|
50
|
-
- `@/components/ui/button` - Button component
|
|
51
|
-
- `@/components/ui/input` - Input component
|
|
52
|
-
- `@/components/ui/label` - Label component
|
|
53
|
-
- `@/components/ui/card` - Card components
|
|
54
|
-
- `@/components/ui/dropdown-menu` - Dropdown menu (for header menu)
|
|
55
|
-
|
|
56
|
-
**Language Files** (Required structure):
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
src/lang/
|
|
60
|
-
├── en/
|
|
61
|
-
│ ├── login.json # Login page translations
|
|
62
|
-
│ ├── register.json # Register page translations
|
|
63
|
-
│ ├── forgotPassword.json # Password reset translations
|
|
64
|
-
│ └── header.json # Header menu translations
|
|
65
|
-
└── tr/
|
|
66
|
-
├── login.json
|
|
67
|
-
├── register.json
|
|
68
|
-
├── forgotPassword.json
|
|
69
|
-
└── header.json
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**Language File Naming Convention**:
|
|
73
|
-
|
|
74
|
-
- Use camelCase: `forgotPassword.json` (NOT `forgot-password.json`)
|
|
75
|
-
- Match the component name passed to `useStaticText("forgotPassword")`
|
|
76
|
-
|
|
77
|
-
### Environment Variables
|
|
78
|
-
|
|
79
|
-
None required (inherits from API module)
|
|
80
|
-
|
|
81
|
-
## Quick Start
|
|
82
|
-
|
|
83
|
-
### 1. Add Routes to App.tsx
|
|
84
|
-
|
|
85
|
-
```tsx
|
|
86
|
-
import Login from "./modules/auth/login-page";
|
|
87
|
-
import Register from "./modules/auth/register-page";
|
|
88
|
-
import ForgotPassword from "./modules/auth/forgot-password-page";
|
|
89
|
-
|
|
90
|
-
<Route path="/login" element={<Login />} />
|
|
91
|
-
<Route path="/register" element={<Register />} />
|
|
92
|
-
<Route path="/forgot-password" element={<ForgotPassword />} />
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 2. Use in Components
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
import { useAuth } from "@/modules/auth/use-auth";
|
|
99
|
-
|
|
100
|
-
function MyComponent() {
|
|
101
|
-
const { user, isAuthenticated, login, logout, api } = useAuth();
|
|
102
|
-
|
|
103
|
-
return isAuthenticated ? (
|
|
104
|
-
<div>
|
|
105
|
-
Welcome, {user?.username} <button onClick={logout}>Logout</button>
|
|
106
|
-
</div>
|
|
107
|
-
) : (
|
|
108
|
-
<button onClick={() => login("username", "password")}>Login</button>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### 3. Add to Header
|
|
114
|
-
|
|
115
|
-
```tsx
|
|
116
|
-
import { AuthHeaderMenu } from "@/modules/auth/auth-header-menu";
|
|
117
|
-
|
|
118
|
-
<AuthHeaderMenu variant="desktop" /> {/* or "mobile" */}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## useAuth Hook API
|
|
122
|
-
|
|
123
|
-
```tsx
|
|
124
|
-
const {
|
|
125
|
-
user, // User: { username, email? }
|
|
126
|
-
isAuthenticated, // boolean
|
|
127
|
-
tokens, // AuthTokens object
|
|
128
|
-
api, // customerClient with token
|
|
129
|
-
login, // (username, password) => Promise<void>
|
|
130
|
-
register, // (username, email, password) => Promise<void>
|
|
131
|
-
confirmEmail, // (username, code) => Promise<void>
|
|
132
|
-
forgotPassword, // (username) => Promise<void>
|
|
133
|
-
resetPassword, // (username, code, newPassword) => Promise<void>
|
|
134
|
-
logout, // () => void
|
|
135
|
-
refreshAccessToken, // () => Promise<boolean>
|
|
136
|
-
} = useAuth();
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## Key Features
|
|
140
|
-
|
|
141
|
-
**Automatic Token Refresh**: Tokens refresh 1 minute before expiration
|
|
142
|
-
**LocalStorage Persistence**: Auth state survives page reloads (key: `auth-storage`)
|
|
143
|
-
**401 Auto-Recovery**: Failed requests auto-retry after token refresh
|
|
144
|
-
**Axios Integration**: Token automatically added to all API calls
|
|
145
|
-
|
|
146
|
-
## Auth Flow Examples
|
|
147
|
-
|
|
148
|
-
### Login & Auto Token Setup
|
|
149
|
-
|
|
150
|
-
```tsx
|
|
151
|
-
await login(username, password);
|
|
152
|
-
// Automatically: stores tokens, updates API client, sets auth state
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Registration with Email Confirmation
|
|
156
|
-
|
|
157
|
-
```tsx
|
|
158
|
-
await register(username, email, password);
|
|
159
|
-
await confirmEmail(username, code); // Code sent to email
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Password Reset
|
|
163
|
-
|
|
164
|
-
```tsx
|
|
165
|
-
await forgotPassword(username); // Request code
|
|
166
|
-
await resetPassword(username, code, newPassword); // Set new password
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Using API with Token
|
|
170
|
-
|
|
171
|
-
```tsx
|
|
172
|
-
const { api } = useAuth();
|
|
173
|
-
const response = await api.orders.getMyOrders(); // Token auto-included
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
## Error Handling
|
|
177
|
-
|
|
178
|
-
```tsx
|
|
179
|
-
import { getErrorMessage } from "@/modules/api/get-error-message";
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
await login(username, password);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
const message = getErrorMessage(err, "Login failed");
|
|
185
|
-
toast.error(message);
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## Translations
|
|
190
|
-
|
|
191
|
-
Language files must follow this naming convention:
|
|
192
|
-
|
|
193
|
-
```
|
|
194
|
-
src/lang/en/
|
|
195
|
-
├── login.json # useStaticText("login")
|
|
196
|
-
├── register.json # useStaticText("register")
|
|
197
|
-
└── forgotPassword.json # useStaticText("forgotPassword")
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Usage in components:
|
|
201
|
-
|
|
202
|
-
```tsx
|
|
203
|
-
const { t } = useStaticText("login"); // File name without .json
|
|
204
|
-
<h1>{t("title")}</h1>;
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
## Direct Store Access
|
|
208
|
-
|
|
209
|
-
```tsx
|
|
210
|
-
import { useAuthStore } from "@/modules/auth/auth-store";
|
|
211
|
-
|
|
212
|
-
const isExpired = useAuthStore((state) => state.isTokenExpired());
|
|
213
|
-
const timeLeft = useAuthStore((state) => state.getTimeUntilExpiry());
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
## Customization
|
|
217
|
-
|
|
218
|
-
**UI Pages**: Fully customizable (login, register, forgot-password pages)
|
|
219
|
-
**Auth Logic**: Should NOT be modified (token refresh, interceptors)
|
|
220
|
-
**Token Timing**: Edit `REFRESH_BUFFER_MS` in `use-auth.ts` (default: 60000ms)
|
|
221
|
-
|
|
222
|
-
## Module Integration Checklist
|
|
223
|
-
|
|
224
|
-
Before using this module, ensure:
|
|
225
|
-
|
|
226
|
-
- ✅ API module is configured (`customer-client.ts`)
|
|
227
|
-
- ✅ `zustand` package installed
|
|
228
|
-
- ✅ All required hooks exist (`useStaticText`, `usePageTitle`)
|
|
229
|
-
- ✅ Language files created (login, register, forgotPassword)
|
|
230
|
-
- ✅ UI components available (Button, Input, Card, etc.)
|
|
231
|
-
- ✅ Routes added to `App.tsx`
|
|
232
|
-
|
|
233
|
-
## Dependencies Summary
|
|
234
|
-
|
|
235
|
-
**Core** (Must not modify):
|
|
236
|
-
|
|
237
|
-
- `zustand` - State management
|
|
238
|
-
- `@/modules/api/customer-client` - API integration
|
|
239
|
-
- `@/hooks/useStaticText` - Translations
|
|
240
|
-
- `auth-store.ts`, `use-auth.ts` - Auth logic
|
|
241
|
-
|
|
242
|
-
**UI** (Customizable):
|
|
243
|
-
|
|
244
|
-
- `react-router-dom` - Navigation
|
|
245
|
-
- `sonner` - Toast notifications
|
|
246
|
-
- `lucide-react` - Icons
|
|
247
|
-
- shadcn/ui components - UI elements
|
|
248
|
-
- Language JSON files - Text content
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import { Link } from "react-router";
|
|
3
|
-
import { User, LogOut } from "lucide-react";
|
|
4
|
-
import { useAuth } from "@/modules/auth/use-auth";
|
|
5
|
-
import { Button } from "@/components/ui/button";
|
|
6
|
-
import {
|
|
7
|
-
DropdownMenu,
|
|
8
|
-
DropdownMenuContent,
|
|
9
|
-
DropdownMenuItem,
|
|
10
|
-
DropdownMenuLabel,
|
|
11
|
-
DropdownMenuSeparator,
|
|
12
|
-
DropdownMenuTrigger,
|
|
13
|
-
} from "@/components/ui/dropdown-menu";
|
|
14
|
-
import { useTranslation } from "react-i18next";
|
|
15
|
-
import { toast } from "sonner";
|
|
16
|
-
|
|
17
|
-
interface AuthHeaderMenuProps {
|
|
18
|
-
children?: ReactNode;
|
|
19
|
-
variant: "desktop" | "mobile";
|
|
20
|
-
onMenuClose?: () => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function AuthHeaderMenu({
|
|
24
|
-
children,
|
|
25
|
-
variant,
|
|
26
|
-
onMenuClose,
|
|
27
|
-
}: AuthHeaderMenuProps) {
|
|
28
|
-
const { isAuthenticated, user, logout } = useAuth();
|
|
29
|
-
const { t } = useTranslation("header");
|
|
30
|
-
|
|
31
|
-
const handleLogout = () => {
|
|
32
|
-
logout();
|
|
33
|
-
toast.success(t("logoutToastTitle", "Goodbye!"), {
|
|
34
|
-
description: t(
|
|
35
|
-
"logoutToastDesc",
|
|
36
|
-
"You have been logged out successfully.",
|
|
37
|
-
),
|
|
38
|
-
});
|
|
39
|
-
onMenuClose?.();
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
if (variant === "desktop") {
|
|
43
|
-
if (isAuthenticated) {
|
|
44
|
-
return (
|
|
45
|
-
<DropdownMenu>
|
|
46
|
-
<DropdownMenuTrigger asChild>
|
|
47
|
-
<Button variant="ghost" size="icon" className="h-10 w-10">
|
|
48
|
-
<User className="h-5 w-5" />
|
|
49
|
-
</Button>
|
|
50
|
-
</DropdownMenuTrigger>
|
|
51
|
-
<DropdownMenuContent align="end" className="w-56">
|
|
52
|
-
<DropdownMenuLabel className="font-normal">
|
|
53
|
-
<div className="flex flex-col space-y-1">
|
|
54
|
-
<p className="text-sm font-medium">{user?.username}</p>
|
|
55
|
-
{user?.email && (
|
|
56
|
-
<p className="text-xs text-muted-foreground">{user.email}</p>
|
|
57
|
-
)}
|
|
58
|
-
</div>
|
|
59
|
-
</DropdownMenuLabel>
|
|
60
|
-
<DropdownMenuSeparator />
|
|
61
|
-
{children}
|
|
62
|
-
{children && <DropdownMenuSeparator />}
|
|
63
|
-
<DropdownMenuItem
|
|
64
|
-
onClick={handleLogout}
|
|
65
|
-
className="text-red-600 focus:text-red-600 focus:bg-red-50 cursor-pointer"
|
|
66
|
-
>
|
|
67
|
-
<LogOut className="mr-2 h-4 w-4" />
|
|
68
|
-
{t("logout", "Logout")}
|
|
69
|
-
</DropdownMenuItem>
|
|
70
|
-
</DropdownMenuContent>
|
|
71
|
-
</DropdownMenu>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<Link to="/login">
|
|
77
|
-
<Button variant="ghost" size="icon" className="h-10 w-10">
|
|
78
|
-
<User className="h-5 w-5" />
|
|
79
|
-
</Button>
|
|
80
|
-
</Link>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Mobile variant
|
|
85
|
-
if (isAuthenticated) {
|
|
86
|
-
return (
|
|
87
|
-
<div className="space-y-3">
|
|
88
|
-
<div className="flex items-center space-x-3 p-3 bg-muted/50 rounded-lg">
|
|
89
|
-
<div className="h-10 w-10 rounded-full bg-primary/10 flex items-center justify-center">
|
|
90
|
-
<User className="h-5 w-5 text-primary" />
|
|
91
|
-
</div>
|
|
92
|
-
<div className="flex-1 min-w-0">
|
|
93
|
-
<p className="text-sm font-medium truncate">{user?.username}</p>
|
|
94
|
-
{user?.email && (
|
|
95
|
-
<p className="text-xs text-muted-foreground truncate">
|
|
96
|
-
{user.email}
|
|
97
|
-
</p>
|
|
98
|
-
)}
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
{children}
|
|
102
|
-
<button
|
|
103
|
-
onClick={handleLogout}
|
|
104
|
-
className="flex items-center space-x-2 text-lg font-medium text-red-600 hover:text-red-700 transition-colors w-full"
|
|
105
|
-
>
|
|
106
|
-
<LogOut className="h-5 w-5" />
|
|
107
|
-
<span>{t("logout", "Logout")}</span>
|
|
108
|
-
</button>
|
|
109
|
-
</div>
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<Link
|
|
115
|
-
to="/login"
|
|
116
|
-
className="flex items-center space-x-2 text-lg font-medium hover:text-primary transition-colors"
|
|
117
|
-
onClick={onMenuClose}
|
|
118
|
-
>
|
|
119
|
-
<User className="h-5 w-5" />
|
|
120
|
-
<span>{t("login", "Login")}</span>
|
|
121
|
-
</Link>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { create } from "zustand";
|
|
2
|
-
import { persist } from "zustand/middleware";
|
|
3
|
-
|
|
4
|
-
export interface User {
|
|
5
|
-
username: string;
|
|
6
|
-
email?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface AuthTokens {
|
|
10
|
-
accessToken: string;
|
|
11
|
-
refreshToken?: string;
|
|
12
|
-
idToken?: string;
|
|
13
|
-
encryptionKey?: string;
|
|
14
|
-
expiresAt?: number; // Unix timestamp in milliseconds
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface AuthState {
|
|
18
|
-
user: User | null;
|
|
19
|
-
tokens: AuthTokens | null;
|
|
20
|
-
isAuthenticated: boolean;
|
|
21
|
-
setAuth: (user: User, tokens: AuthTokens) => void;
|
|
22
|
-
updateTokens: (tokens: AuthTokens) => void;
|
|
23
|
-
clearAuth: () => void;
|
|
24
|
-
isTokenExpired: () => boolean;
|
|
25
|
-
getTimeUntilExpiry: () => number | null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const useAuthStore = create<AuthState>()(
|
|
29
|
-
persist(
|
|
30
|
-
(set, get) => ({
|
|
31
|
-
user: null,
|
|
32
|
-
tokens: null,
|
|
33
|
-
isAuthenticated: false,
|
|
34
|
-
|
|
35
|
-
setAuth: (user, tokens) => set({ user, tokens, isAuthenticated: true }),
|
|
36
|
-
|
|
37
|
-
updateTokens: (tokens) => set({ tokens }),
|
|
38
|
-
|
|
39
|
-
clearAuth: () =>
|
|
40
|
-
set({ user: null, tokens: null, isAuthenticated: false }),
|
|
41
|
-
|
|
42
|
-
isTokenExpired: () => {
|
|
43
|
-
const { tokens } = get();
|
|
44
|
-
if (!tokens?.expiresAt) return false;
|
|
45
|
-
// Consider token expired 30 seconds before actual expiry for safety margin
|
|
46
|
-
return Date.now() >= tokens.expiresAt - 30000;
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
getTimeUntilExpiry: () => {
|
|
50
|
-
const { tokens } = get();
|
|
51
|
-
if (!tokens?.expiresAt) return null;
|
|
52
|
-
return tokens.expiresAt - Date.now();
|
|
53
|
-
},
|
|
54
|
-
}),
|
|
55
|
-
{ name: "auth-storage" },
|
|
56
|
-
),
|
|
57
|
-
);
|