@cortexmemory/cli 0.26.2 → 0.27.3
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/commands/convex.js +1 -1
- package/dist/commands/convex.js.map +1 -1
- package/dist/commands/deploy.d.ts +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +771 -144
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +210 -36
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +273 -43
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +102 -46
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +94 -7
- package/dist/commands/status.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/app-template-sync.d.ts +95 -0
- package/dist/utils/app-template-sync.d.ts.map +1 -0
- package/dist/utils/app-template-sync.js +425 -0
- package/dist/utils/app-template-sync.js.map +1 -0
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +20 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/deployment-selector.d.ts +21 -0
- package/dist/utils/deployment-selector.d.ts.map +1 -1
- package/dist/utils/deployment-selector.js +32 -0
- package/dist/utils/deployment-selector.js.map +1 -1
- package/dist/utils/init/graph-setup.d.ts.map +1 -1
- package/dist/utils/init/graph-setup.js +25 -2
- package/dist/utils/init/graph-setup.js.map +1 -1
- package/dist/utils/init/quickstart-setup.d.ts +87 -0
- package/dist/utils/init/quickstart-setup.d.ts.map +1 -0
- package/dist/utils/init/quickstart-setup.js +462 -0
- package/dist/utils/init/quickstart-setup.js.map +1 -0
- package/dist/utils/schema-sync.d.ts.map +1 -1
- package/dist/utils/schema-sync.js +27 -21
- package/dist/utils/schema-sync.js.map +1 -1
- package/package.json +3 -2
- package/templates/vercel-ai-quickstart/.env.local.example +45 -0
- package/templates/vercel-ai-quickstart/README.md +280 -0
- package/templates/vercel-ai-quickstart/app/api/auth/check/route.ts +30 -0
- package/templates/vercel-ai-quickstart/app/api/auth/login/route.ts +83 -0
- package/templates/vercel-ai-quickstart/app/api/auth/register/route.ts +94 -0
- package/templates/vercel-ai-quickstart/app/api/auth/setup/route.ts +59 -0
- package/templates/vercel-ai-quickstart/app/api/chat/route.ts +277 -0
- package/templates/vercel-ai-quickstart/app/api/conversations/route.ts +179 -0
- package/templates/vercel-ai-quickstart/app/api/facts/route.ts +39 -0
- package/templates/vercel-ai-quickstart/app/api/health/route.ts +99 -0
- package/templates/vercel-ai-quickstart/app/api/memories/route.ts +37 -0
- package/templates/vercel-ai-quickstart/app/globals.css +275 -0
- package/templates/vercel-ai-quickstart/app/layout.tsx +19 -0
- package/templates/vercel-ai-quickstart/app/page.tsx +216 -0
- package/templates/vercel-ai-quickstart/components/AdminSetup.tsx +139 -0
- package/templates/vercel-ai-quickstart/components/AuthProvider.tsx +283 -0
- package/templates/vercel-ai-quickstart/components/ChatHistorySidebar.tsx +323 -0
- package/templates/vercel-ai-quickstart/components/ChatInterface.tsx +334 -0
- package/templates/vercel-ai-quickstart/components/ConvexClientProvider.tsx +21 -0
- package/templates/vercel-ai-quickstart/components/DataPreview.tsx +57 -0
- package/templates/vercel-ai-quickstart/components/HealthStatus.tsx +214 -0
- package/templates/vercel-ai-quickstart/components/LayerCard.tsx +263 -0
- package/templates/vercel-ai-quickstart/components/LayerFlowDiagram.tsx +195 -0
- package/templates/vercel-ai-quickstart/components/LoginScreen.tsx +202 -0
- package/templates/vercel-ai-quickstart/components/MemorySpaceSwitcher.tsx +93 -0
- package/templates/vercel-ai-quickstart/convex/conversations.ts +67 -0
- package/templates/vercel-ai-quickstart/convex/facts.ts +131 -0
- package/templates/vercel-ai-quickstart/convex/health.ts +15 -0
- package/templates/vercel-ai-quickstart/convex/memories.ts +104 -0
- package/templates/vercel-ai-quickstart/convex/schema.ts +20 -0
- package/templates/vercel-ai-quickstart/convex/users.ts +105 -0
- package/templates/vercel-ai-quickstart/jest.config.js +45 -0
- package/templates/vercel-ai-quickstart/lib/animations.ts +146 -0
- package/templates/vercel-ai-quickstart/lib/cortex.ts +27 -0
- package/templates/vercel-ai-quickstart/lib/layer-tracking.ts +214 -0
- package/templates/vercel-ai-quickstart/lib/password.ts +120 -0
- package/templates/vercel-ai-quickstart/next.config.js +27 -0
- package/templates/vercel-ai-quickstart/package.json +46 -0
- package/templates/vercel-ai-quickstart/postcss.config.js +5 -0
- package/templates/vercel-ai-quickstart/tailwind.config.js +37 -0
- package/templates/vercel-ai-quickstart/tests/helpers/mock-cortex.ts +263 -0
- package/templates/vercel-ai-quickstart/tests/helpers/setup.ts +48 -0
- package/templates/vercel-ai-quickstart/tests/integration/auth.test.ts +455 -0
- package/templates/vercel-ai-quickstart/tests/integration/conversations.test.ts +461 -0
- package/templates/vercel-ai-quickstart/tests/unit/password.test.ts +228 -0
- package/templates/vercel-ai-quickstart/tsconfig.json +33 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useAuth } from "./AuthProvider";
|
|
5
|
+
|
|
6
|
+
export function AdminSetup() {
|
|
7
|
+
const { setupAdmin, error, clearError } = useAuth();
|
|
8
|
+
const [password, setPassword] = useState("");
|
|
9
|
+
const [confirmPassword, setConfirmPassword] = useState("");
|
|
10
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
11
|
+
const [localError, setLocalError] = useState<string | null>(null);
|
|
12
|
+
|
|
13
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
setLocalError(null);
|
|
16
|
+
clearError();
|
|
17
|
+
|
|
18
|
+
if (password.length < 4) {
|
|
19
|
+
setLocalError("Password must be at least 4 characters");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (password !== confirmPassword) {
|
|
24
|
+
setLocalError("Passwords do not match");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setIsLoading(true);
|
|
29
|
+
await setupAdmin(password);
|
|
30
|
+
setIsLoading(false);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const displayError = localError || error;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="min-h-screen flex items-center justify-center p-4">
|
|
37
|
+
<div className="w-full max-w-md">
|
|
38
|
+
{/* Logo and Title */}
|
|
39
|
+
<div className="text-center mb-8">
|
|
40
|
+
<div className="w-20 h-20 mx-auto mb-6 rounded-2xl bg-gradient-to-br from-cortex-500 to-cortex-700 flex items-center justify-center shadow-lg shadow-cortex-500/20">
|
|
41
|
+
<span className="text-4xl">🧠</span>
|
|
42
|
+
</div>
|
|
43
|
+
<h1 className="text-3xl font-bold mb-2">Welcome to Cortex</h1>
|
|
44
|
+
<p className="text-gray-400">Set up your admin password to get started</p>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Setup Form */}
|
|
48
|
+
<div className="glass rounded-2xl p-8">
|
|
49
|
+
<form onSubmit={handleSubmit} className="space-y-6">
|
|
50
|
+
<div>
|
|
51
|
+
<label
|
|
52
|
+
htmlFor="password"
|
|
53
|
+
className="block text-sm font-medium text-gray-300 mb-2"
|
|
54
|
+
>
|
|
55
|
+
Admin Password
|
|
56
|
+
</label>
|
|
57
|
+
<input
|
|
58
|
+
id="password"
|
|
59
|
+
type="password"
|
|
60
|
+
value={password}
|
|
61
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
62
|
+
placeholder="Enter a secure password"
|
|
63
|
+
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:border-cortex-500 transition-colors"
|
|
64
|
+
disabled={isLoading}
|
|
65
|
+
autoFocus
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div>
|
|
70
|
+
<label
|
|
71
|
+
htmlFor="confirmPassword"
|
|
72
|
+
className="block text-sm font-medium text-gray-300 mb-2"
|
|
73
|
+
>
|
|
74
|
+
Confirm Password
|
|
75
|
+
</label>
|
|
76
|
+
<input
|
|
77
|
+
id="confirmPassword"
|
|
78
|
+
type="password"
|
|
79
|
+
value={confirmPassword}
|
|
80
|
+
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
81
|
+
placeholder="Confirm your password"
|
|
82
|
+
className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl focus:outline-none focus:border-cortex-500 transition-colors"
|
|
83
|
+
disabled={isLoading}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
{displayError && (
|
|
88
|
+
<div className="p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-400 text-sm">
|
|
89
|
+
{displayError}
|
|
90
|
+
</div>
|
|
91
|
+
)}
|
|
92
|
+
|
|
93
|
+
<button
|
|
94
|
+
type="submit"
|
|
95
|
+
disabled={isLoading || !password || !confirmPassword}
|
|
96
|
+
className="w-full py-3 bg-cortex-600 hover:bg-cortex-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-xl font-medium transition-colors flex items-center justify-center gap-2"
|
|
97
|
+
>
|
|
98
|
+
{isLoading ? (
|
|
99
|
+
<>
|
|
100
|
+
<svg
|
|
101
|
+
className="animate-spin h-5 w-5"
|
|
102
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
103
|
+
fill="none"
|
|
104
|
+
viewBox="0 0 24 24"
|
|
105
|
+
>
|
|
106
|
+
<circle
|
|
107
|
+
className="opacity-25"
|
|
108
|
+
cx="12"
|
|
109
|
+
cy="12"
|
|
110
|
+
r="10"
|
|
111
|
+
stroke="currentColor"
|
|
112
|
+
strokeWidth="4"
|
|
113
|
+
/>
|
|
114
|
+
<path
|
|
115
|
+
className="opacity-75"
|
|
116
|
+
fill="currentColor"
|
|
117
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
118
|
+
/>
|
|
119
|
+
</svg>
|
|
120
|
+
Setting up...
|
|
121
|
+
</>
|
|
122
|
+
) : (
|
|
123
|
+
"Set Admin Password"
|
|
124
|
+
)}
|
|
125
|
+
</button>
|
|
126
|
+
</form>
|
|
127
|
+
|
|
128
|
+
<div className="mt-6 pt-6 border-t border-white/10">
|
|
129
|
+
<p className="text-xs text-gray-500 text-center">
|
|
130
|
+
This password will be used to manage the quickstart demo.
|
|
131
|
+
<br />
|
|
132
|
+
You can create additional user accounts after setup.
|
|
133
|
+
</p>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
useState,
|
|
7
|
+
useEffect,
|
|
8
|
+
useCallback,
|
|
9
|
+
type ReactNode,
|
|
10
|
+
} from "react";
|
|
11
|
+
|
|
12
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
13
|
+
// Types
|
|
14
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
15
|
+
|
|
16
|
+
export interface User {
|
|
17
|
+
id: string;
|
|
18
|
+
displayName: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AuthState {
|
|
22
|
+
isLoading: boolean;
|
|
23
|
+
isAdminSetup: boolean | null;
|
|
24
|
+
isAuthenticated: boolean;
|
|
25
|
+
user: User | null;
|
|
26
|
+
error: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AuthContextValue extends AuthState {
|
|
30
|
+
setupAdmin: (password: string) => Promise<boolean>;
|
|
31
|
+
login: (username: string, password: string) => Promise<boolean>;
|
|
32
|
+
register: (
|
|
33
|
+
username: string,
|
|
34
|
+
password: string,
|
|
35
|
+
displayName?: string
|
|
36
|
+
) => Promise<boolean>;
|
|
37
|
+
logout: () => void;
|
|
38
|
+
clearError: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
42
|
+
// Context
|
|
43
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
44
|
+
|
|
45
|
+
const AuthContext = createContext<AuthContextValue | null>(null);
|
|
46
|
+
|
|
47
|
+
const STORAGE_KEY = "cortex-quickstart-auth";
|
|
48
|
+
|
|
49
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
50
|
+
// Provider
|
|
51
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
52
|
+
|
|
53
|
+
export function AuthProvider({ children }: { children: ReactNode }) {
|
|
54
|
+
const [state, setState] = useState<AuthState>({
|
|
55
|
+
isLoading: true,
|
|
56
|
+
isAdminSetup: null,
|
|
57
|
+
isAuthenticated: false,
|
|
58
|
+
user: null,
|
|
59
|
+
error: null,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Check admin setup status and restore session on mount
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const init = async () => {
|
|
65
|
+
try {
|
|
66
|
+
// Check if admin is set up
|
|
67
|
+
const checkResponse = await fetch("/api/auth/check");
|
|
68
|
+
const checkData = await checkResponse.json();
|
|
69
|
+
|
|
70
|
+
// Try to restore session from localStorage
|
|
71
|
+
let user: User | null = null;
|
|
72
|
+
let sessionToken: string | null = null;
|
|
73
|
+
|
|
74
|
+
if (typeof window !== "undefined") {
|
|
75
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
76
|
+
if (stored) {
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(stored);
|
|
79
|
+
user = parsed.user;
|
|
80
|
+
sessionToken = parsed.sessionToken;
|
|
81
|
+
} catch {
|
|
82
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setState({
|
|
88
|
+
isLoading: false,
|
|
89
|
+
isAdminSetup: checkData.isSetup,
|
|
90
|
+
isAuthenticated: !!user && !!sessionToken,
|
|
91
|
+
user,
|
|
92
|
+
error: null,
|
|
93
|
+
});
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error("Auth init error:", error);
|
|
96
|
+
setState((prev) => ({
|
|
97
|
+
...prev,
|
|
98
|
+
isLoading: false,
|
|
99
|
+
error: "Failed to initialize authentication",
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
init();
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
// Setup admin password
|
|
108
|
+
const setupAdmin = useCallback(async (password: string): Promise<boolean> => {
|
|
109
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch("/api/auth/setup", {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: { "Content-Type": "application/json" },
|
|
115
|
+
body: JSON.stringify({ password }),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const data = await response.json();
|
|
119
|
+
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
setState((prev) => ({ ...prev, error: data.error }));
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
setState((prev) => ({
|
|
126
|
+
...prev,
|
|
127
|
+
isAdminSetup: true,
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
return true;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error("Setup error:", error);
|
|
133
|
+
setState((prev) => ({
|
|
134
|
+
...prev,
|
|
135
|
+
error: "Failed to setup admin password",
|
|
136
|
+
}));
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}, []);
|
|
140
|
+
|
|
141
|
+
// Login
|
|
142
|
+
const login = useCallback(
|
|
143
|
+
async (username: string, password: string): Promise<boolean> => {
|
|
144
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch("/api/auth/login", {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: { "Content-Type": "application/json" },
|
|
150
|
+
body: JSON.stringify({ username, password }),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const data = await response.json();
|
|
154
|
+
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
setState((prev) => ({ ...prev, error: data.error }));
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Store session
|
|
161
|
+
if (typeof window !== "undefined") {
|
|
162
|
+
localStorage.setItem(
|
|
163
|
+
STORAGE_KEY,
|
|
164
|
+
JSON.stringify({
|
|
165
|
+
user: data.user,
|
|
166
|
+
sessionToken: data.sessionToken,
|
|
167
|
+
})
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setState((prev) => ({
|
|
172
|
+
...prev,
|
|
173
|
+
isAuthenticated: true,
|
|
174
|
+
user: data.user,
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
return true;
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error("Login error:", error);
|
|
180
|
+
setState((prev) => ({
|
|
181
|
+
...prev,
|
|
182
|
+
error: "Failed to login",
|
|
183
|
+
}));
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
[]
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Register
|
|
191
|
+
const register = useCallback(
|
|
192
|
+
async (
|
|
193
|
+
username: string,
|
|
194
|
+
password: string,
|
|
195
|
+
displayName?: string
|
|
196
|
+
): Promise<boolean> => {
|
|
197
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const response = await fetch("/api/auth/register", {
|
|
201
|
+
method: "POST",
|
|
202
|
+
headers: { "Content-Type": "application/json" },
|
|
203
|
+
body: JSON.stringify({ username, password, displayName }),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const data = await response.json();
|
|
207
|
+
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
setState((prev) => ({ ...prev, error: data.error }));
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Store session
|
|
214
|
+
if (typeof window !== "undefined") {
|
|
215
|
+
localStorage.setItem(
|
|
216
|
+
STORAGE_KEY,
|
|
217
|
+
JSON.stringify({
|
|
218
|
+
user: data.user,
|
|
219
|
+
sessionToken: data.sessionToken,
|
|
220
|
+
})
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
setState((prev) => ({
|
|
225
|
+
...prev,
|
|
226
|
+
isAuthenticated: true,
|
|
227
|
+
user: data.user,
|
|
228
|
+
}));
|
|
229
|
+
|
|
230
|
+
return true;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error("Register error:", error);
|
|
233
|
+
setState((prev) => ({
|
|
234
|
+
...prev,
|
|
235
|
+
error: "Failed to register",
|
|
236
|
+
}));
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
[]
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
// Logout
|
|
244
|
+
const logout = useCallback(() => {
|
|
245
|
+
if (typeof window !== "undefined") {
|
|
246
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
setState((prev) => ({
|
|
250
|
+
...prev,
|
|
251
|
+
isAuthenticated: false,
|
|
252
|
+
user: null,
|
|
253
|
+
}));
|
|
254
|
+
}, []);
|
|
255
|
+
|
|
256
|
+
// Clear error
|
|
257
|
+
const clearError = useCallback(() => {
|
|
258
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
259
|
+
}, []);
|
|
260
|
+
|
|
261
|
+
const value: AuthContextValue = {
|
|
262
|
+
...state,
|
|
263
|
+
setupAdmin,
|
|
264
|
+
login,
|
|
265
|
+
register,
|
|
266
|
+
logout,
|
|
267
|
+
clearError,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
274
|
+
// Hook
|
|
275
|
+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
276
|
+
|
|
277
|
+
export function useAuth(): AuthContextValue {
|
|
278
|
+
const context = useContext(AuthContext);
|
|
279
|
+
if (!context) {
|
|
280
|
+
throw new Error("useAuth must be used within an AuthProvider");
|
|
281
|
+
}
|
|
282
|
+
return context;
|
|
283
|
+
}
|