@bagelink/auth 1.1.39
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/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/index.cjs +269 -0
- package/dist/index.d.cts +74 -0
- package/dist/index.d.mts +74 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.mjs +256 -0
- package/package.json +52 -0
- package/src/api/api.ts +24 -0
- package/src/api/auth.ts +81 -0
- package/src/composable/useAuth.ts +222 -0
- package/src/index.ts +3 -0
- package/src/types.ts +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Neveh Allon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @bagelink/auth
|
|
2
|
+
|
|
3
|
+
A framework-agnostic authentication composable with Vue support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Framework-agnostic reactivity system
|
|
8
|
+
- Automatic Vue integration
|
|
9
|
+
- TypeScript support
|
|
10
|
+
- Complete authentication flow (login, signup, password recovery, etc.)
|
|
11
|
+
- Error handling
|
|
12
|
+
- User management
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun add @bagelink/auth
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Basic Setup
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { initAuth } from '@bagelink/auth'
|
|
26
|
+
|
|
27
|
+
// Initialize with axios instance
|
|
28
|
+
const auth = initAuth({
|
|
29
|
+
axios: axiosInstance,
|
|
30
|
+
errorHandler: (error) => console.error(error)
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Vue Integration
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// In your main.ts
|
|
38
|
+
import { createApp } from 'vue'
|
|
39
|
+
import { initAuth } from '@bagelink/auth'
|
|
40
|
+
|
|
41
|
+
const app = createApp(App)
|
|
42
|
+
|
|
43
|
+
// Initialize auth
|
|
44
|
+
const auth = initAuth({
|
|
45
|
+
axios: axiosInstance,
|
|
46
|
+
errorHandler: (error) => console.error(error)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Use as a plugin
|
|
50
|
+
app.use(auth)
|
|
51
|
+
|
|
52
|
+
// In your components
|
|
53
|
+
import { useAuth } from '@bagelink/auth'
|
|
54
|
+
|
|
55
|
+
export default {
|
|
56
|
+
setup() {
|
|
57
|
+
const { login, currentUser, getIsLoggedIn } = useAuth()
|
|
58
|
+
|
|
59
|
+
// Use auth methods
|
|
60
|
+
const handleLogin = async () => {
|
|
61
|
+
await login({
|
|
62
|
+
email: 'user@example.com',
|
|
63
|
+
password: 'password'
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
currentUser,
|
|
69
|
+
getIsLoggedIn,
|
|
70
|
+
handleLogin
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Vanilla JavaScript
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { initAuth } from '@bagelink/auth'
|
|
80
|
+
|
|
81
|
+
const auth = initAuth({
|
|
82
|
+
axios: axiosInstance,
|
|
83
|
+
errorHandler: (error) => console.error(error)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const { login, currentUser, getIsLoggedIn } = auth.useAuth()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## API
|
|
90
|
+
|
|
91
|
+
### State
|
|
92
|
+
|
|
93
|
+
- `currentUser`: Current user information
|
|
94
|
+
- `passwordForm`: Password update form state
|
|
95
|
+
|
|
96
|
+
### Getters
|
|
97
|
+
|
|
98
|
+
- `getFullName()`: Get user's full name
|
|
99
|
+
- `getIsLoggedIn()`: Check if user is logged in
|
|
100
|
+
|
|
101
|
+
### Actions
|
|
102
|
+
|
|
103
|
+
- `login(credentials)`: Login with email and password
|
|
104
|
+
- `logout()`: Logout current user
|
|
105
|
+
- `checkAuth()`: Check authentication status
|
|
106
|
+
- `signup(user)`: Register new user
|
|
107
|
+
- `recoverPassword(email)`: Request password recovery
|
|
108
|
+
- `resetPassword(form)`: Reset password
|
|
109
|
+
- `updatePassword()`: Update current user's password
|
|
110
|
+
- `updateProfile(user)`: Update user profile
|
|
111
|
+
- `toggleUserStatus(userId, isActive)`: Toggle user active status
|
|
112
|
+
- `deleteUser(userId)`: Delete user
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('axios');
|
|
4
|
+
|
|
5
|
+
let api$1 = null;
|
|
6
|
+
function getApi() {
|
|
7
|
+
if (!api$1) {
|
|
8
|
+
throw new Error("API not initialized. Call initApi first.");
|
|
9
|
+
}
|
|
10
|
+
return api$1;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ax = getApi();
|
|
14
|
+
ax.interceptors.request.use((config) => {
|
|
15
|
+
const token = localStorage.getItem("access_token");
|
|
16
|
+
if (token !== null && config.headers) {
|
|
17
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
18
|
+
}
|
|
19
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
20
|
+
const resetToken = urlParams.get("token");
|
|
21
|
+
if (resetToken !== null && config.headers) {
|
|
22
|
+
config.headers.Authorization = `Bearer ${resetToken}`;
|
|
23
|
+
}
|
|
24
|
+
return config;
|
|
25
|
+
});
|
|
26
|
+
async function login(username, password) {
|
|
27
|
+
const { data } = await ax.post("/auth/login", {
|
|
28
|
+
username: username.toLowerCase(),
|
|
29
|
+
password
|
|
30
|
+
});
|
|
31
|
+
localStorage.setItem("access_token", data.access_token);
|
|
32
|
+
}
|
|
33
|
+
function logout() {
|
|
34
|
+
localStorage.removeItem("access_token");
|
|
35
|
+
window.location.reload();
|
|
36
|
+
}
|
|
37
|
+
async function passwordRecovery(email) {
|
|
38
|
+
return ax.post(`/auth/password-recovery`, { email });
|
|
39
|
+
}
|
|
40
|
+
async function resetPassword(newPassword) {
|
|
41
|
+
return ax.post("/auth/reset-password", { new_password: newPassword });
|
|
42
|
+
}
|
|
43
|
+
async function getCurrentUser() {
|
|
44
|
+
return ax.get("/auth/me");
|
|
45
|
+
}
|
|
46
|
+
async function signup(user) {
|
|
47
|
+
return ax.post("/auth/signup", {
|
|
48
|
+
email: user.email.toLowerCase(),
|
|
49
|
+
password: user.password,
|
|
50
|
+
first_name: user.first_name,
|
|
51
|
+
last_name: user.last_name
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function updatePassword(form) {
|
|
55
|
+
return ax.put("/auth/password", {
|
|
56
|
+
current_password: form.current_password,
|
|
57
|
+
new_password: form.new_password
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async function updateUserProfile(user) {
|
|
61
|
+
return ax.put("/auth/profile", user);
|
|
62
|
+
}
|
|
63
|
+
async function setUserStatus(userId, isActive) {
|
|
64
|
+
return ax.patch(`/auth/users/${userId}/status`, { is_active: isActive });
|
|
65
|
+
}
|
|
66
|
+
async function deleteUser(userId) {
|
|
67
|
+
return ax.delete(`/auth/users/${userId}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let api = null;
|
|
71
|
+
let onError = null;
|
|
72
|
+
let createRef = null;
|
|
73
|
+
const defaultReactiveFactory = (initial) => {
|
|
74
|
+
let value = initial;
|
|
75
|
+
return {
|
|
76
|
+
get value() {
|
|
77
|
+
return value;
|
|
78
|
+
},
|
|
79
|
+
set: (newValue) => {
|
|
80
|
+
value = newValue;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
function createVueReactiveFactory(app) {
|
|
85
|
+
return (initial) => {
|
|
86
|
+
const refValue = app.ref(initial);
|
|
87
|
+
return {
|
|
88
|
+
get value() {
|
|
89
|
+
return refValue.value;
|
|
90
|
+
},
|
|
91
|
+
set: (v) => {
|
|
92
|
+
refValue.value = v;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function initAuth({
|
|
98
|
+
axios,
|
|
99
|
+
errorHandler,
|
|
100
|
+
reactive = defaultReactiveFactory
|
|
101
|
+
} = {}) {
|
|
102
|
+
api = axios || null;
|
|
103
|
+
onError = errorHandler || null;
|
|
104
|
+
createRef = reactive;
|
|
105
|
+
return {
|
|
106
|
+
install(app) {
|
|
107
|
+
if (app.ref) {
|
|
108
|
+
createRef = createVueReactiveFactory(app);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
useAuth
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function useAuth() {
|
|
115
|
+
if (!api || !createRef) {
|
|
116
|
+
throw new Error("Auth composable not initialized. Call initAuth first.");
|
|
117
|
+
}
|
|
118
|
+
const currentUser = createRef({
|
|
119
|
+
id: "",
|
|
120
|
+
email: "",
|
|
121
|
+
first_name: "",
|
|
122
|
+
last_name: "",
|
|
123
|
+
is_superuser: false,
|
|
124
|
+
is_active: false
|
|
125
|
+
});
|
|
126
|
+
const passwordForm = createRef({
|
|
127
|
+
current_password: "",
|
|
128
|
+
new_password: "",
|
|
129
|
+
confirmNewPassword: ""
|
|
130
|
+
});
|
|
131
|
+
const getFullName = () => `${currentUser.value.first_name} ${currentUser.value.last_name}`;
|
|
132
|
+
const getIsLoggedIn = () => currentUser.value.id.length > 0;
|
|
133
|
+
function handleError(error) {
|
|
134
|
+
if (onError) {
|
|
135
|
+
onError(error);
|
|
136
|
+
}
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
async function logout$1() {
|
|
140
|
+
try {
|
|
141
|
+
await logout();
|
|
142
|
+
} catch (error) {
|
|
143
|
+
handleError(error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function login$1(credentials) {
|
|
147
|
+
const email = credentials.email.toLowerCase();
|
|
148
|
+
const { password } = credentials;
|
|
149
|
+
try {
|
|
150
|
+
await login(email, password);
|
|
151
|
+
await checkAuth();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
handleError(error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function checkAuth() {
|
|
157
|
+
try {
|
|
158
|
+
if (!getIsLoggedIn()) {
|
|
159
|
+
const { data } = await getCurrentUser();
|
|
160
|
+
currentUser.set(data);
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return getIsLoggedIn();
|
|
166
|
+
}
|
|
167
|
+
async function signup$1(user) {
|
|
168
|
+
try {
|
|
169
|
+
if (user.password !== user.confirmPassword) {
|
|
170
|
+
throw new Error("Passwords do not match");
|
|
171
|
+
}
|
|
172
|
+
const { data } = await signup(user);
|
|
173
|
+
currentUser.set(data);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
handleError(error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function recoverPassword(email) {
|
|
179
|
+
try {
|
|
180
|
+
await passwordRecovery(email);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
handleError(error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function resetPassword$1(form) {
|
|
186
|
+
try {
|
|
187
|
+
if (form.new_password !== form.confirmNewPassword) {
|
|
188
|
+
throw new Error("Passwords do not match");
|
|
189
|
+
}
|
|
190
|
+
await resetPassword(form.new_password);
|
|
191
|
+
form = {
|
|
192
|
+
current_password: "",
|
|
193
|
+
new_password: "",
|
|
194
|
+
confirmNewPassword: ""
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
handleError(error);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function updatePassword$1() {
|
|
201
|
+
try {
|
|
202
|
+
if (passwordForm.value.new_password !== passwordForm.value.confirmNewPassword) {
|
|
203
|
+
throw new Error("Passwords do not match");
|
|
204
|
+
}
|
|
205
|
+
await updatePassword(passwordForm.value);
|
|
206
|
+
passwordForm.set({
|
|
207
|
+
current_password: "",
|
|
208
|
+
new_password: "",
|
|
209
|
+
confirmNewPassword: ""
|
|
210
|
+
});
|
|
211
|
+
} catch (error) {
|
|
212
|
+
handleError(error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function updateProfile(user) {
|
|
216
|
+
try {
|
|
217
|
+
const { data } = await updateUserProfile(user);
|
|
218
|
+
currentUser.set({ ...currentUser.value, ...data });
|
|
219
|
+
} catch (error) {
|
|
220
|
+
handleError(error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function toggleUserStatus(userId, isActive) {
|
|
224
|
+
try {
|
|
225
|
+
await setUserStatus(userId, isActive);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
handleError(error);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function deleteUser$1(userId) {
|
|
231
|
+
try {
|
|
232
|
+
await deleteUser(userId);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
handleError(error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
// State
|
|
239
|
+
currentUser,
|
|
240
|
+
passwordForm,
|
|
241
|
+
// Getters
|
|
242
|
+
getFullName,
|
|
243
|
+
getIsLoggedIn,
|
|
244
|
+
// Actions
|
|
245
|
+
logout: logout$1,
|
|
246
|
+
login: login$1,
|
|
247
|
+
checkAuth,
|
|
248
|
+
signup: signup$1,
|
|
249
|
+
recoverPassword,
|
|
250
|
+
resetPassword: resetPassword$1,
|
|
251
|
+
updatePassword: updatePassword$1,
|
|
252
|
+
updateProfile,
|
|
253
|
+
toggleUserStatus,
|
|
254
|
+
deleteUser: deleteUser$1
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
exports.deleteUser = deleteUser;
|
|
259
|
+
exports.getCurrentUser = getCurrentUser;
|
|
260
|
+
exports.initAuth = initAuth;
|
|
261
|
+
exports.login = login;
|
|
262
|
+
exports.logout = logout;
|
|
263
|
+
exports.passwordRecovery = passwordRecovery;
|
|
264
|
+
exports.resetPassword = resetPassword;
|
|
265
|
+
exports.setUserStatus = setUserStatus;
|
|
266
|
+
exports.signup = signup;
|
|
267
|
+
exports.updatePassword = updatePassword;
|
|
268
|
+
exports.updateUserProfile = updateUserProfile;
|
|
269
|
+
exports.useAuth = useAuth;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { AxiosResponse, AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
interface User {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
first_name: string;
|
|
7
|
+
last_name: string;
|
|
8
|
+
is_superuser: boolean;
|
|
9
|
+
is_active: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface UserRegister {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
first_name: string;
|
|
15
|
+
last_name: string;
|
|
16
|
+
}
|
|
17
|
+
interface NewUser extends UserRegister {
|
|
18
|
+
confirmPassword: string;
|
|
19
|
+
}
|
|
20
|
+
interface UpdatePasswordForm {
|
|
21
|
+
current_password: string;
|
|
22
|
+
new_password: string;
|
|
23
|
+
confirmNewPassword: string;
|
|
24
|
+
}
|
|
25
|
+
interface ReactiveRef<T> {
|
|
26
|
+
value: T;
|
|
27
|
+
set: (newValue: T) => void;
|
|
28
|
+
}
|
|
29
|
+
type ReactiveFactory = <T>(initial: T) => ReactiveRef<T>;
|
|
30
|
+
|
|
31
|
+
interface NewPasswordPayload {
|
|
32
|
+
token: string;
|
|
33
|
+
new_password: string;
|
|
34
|
+
}
|
|
35
|
+
declare function login(username: string, password: string): Promise<void>;
|
|
36
|
+
declare function logout(): void;
|
|
37
|
+
declare function passwordRecovery(email?: string): Promise<AxiosResponse>;
|
|
38
|
+
declare function resetPassword(newPassword: NewPasswordPayload['new_password']): Promise<AxiosResponse>;
|
|
39
|
+
declare function getCurrentUser(): Promise<AxiosResponse<User>>;
|
|
40
|
+
declare function signup(user: NewUser): Promise<AxiosResponse<User>>;
|
|
41
|
+
declare function updatePassword(form: UpdatePasswordForm): Promise<AxiosResponse>;
|
|
42
|
+
declare function updateUserProfile(user: Partial<User>): Promise<AxiosResponse<User>>;
|
|
43
|
+
declare function setUserStatus(userId: string, isActive: boolean): Promise<AxiosResponse>;
|
|
44
|
+
declare function deleteUser(userId: string): Promise<AxiosResponse>;
|
|
45
|
+
|
|
46
|
+
declare function initAuth({ axios, errorHandler, reactive }?: {
|
|
47
|
+
axios?: AxiosInstance;
|
|
48
|
+
errorHandler?: (error: any) => void;
|
|
49
|
+
reactive?: ReactiveFactory;
|
|
50
|
+
}): {
|
|
51
|
+
install(app: any): void;
|
|
52
|
+
useAuth: typeof useAuth;
|
|
53
|
+
};
|
|
54
|
+
declare function useAuth(): {
|
|
55
|
+
currentUser: ReactiveRef<User>;
|
|
56
|
+
passwordForm: ReactiveRef<UpdatePasswordForm>;
|
|
57
|
+
getFullName: () => string;
|
|
58
|
+
getIsLoggedIn: () => boolean;
|
|
59
|
+
logout: () => Promise<void>;
|
|
60
|
+
login: (credentials: {
|
|
61
|
+
email: string;
|
|
62
|
+
password: string;
|
|
63
|
+
}) => Promise<void>;
|
|
64
|
+
checkAuth: () => Promise<boolean>;
|
|
65
|
+
signup: (user: NewUser) => Promise<void>;
|
|
66
|
+
recoverPassword: (email: string) => Promise<void>;
|
|
67
|
+
resetPassword: (form: UpdatePasswordForm) => Promise<void>;
|
|
68
|
+
updatePassword: () => Promise<void>;
|
|
69
|
+
updateProfile: (user: Partial<User>) => Promise<void>;
|
|
70
|
+
toggleUserStatus: (userId: string, isActive: boolean) => Promise<void>;
|
|
71
|
+
deleteUser: (userId: string) => Promise<void>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export { type NewPasswordPayload, type NewUser, type ReactiveFactory, type ReactiveRef, type UpdatePasswordForm, type User, type UserRegister, deleteUser, getCurrentUser, initAuth, login, logout, passwordRecovery, resetPassword, setUserStatus, signup, updatePassword, updateUserProfile, useAuth };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { AxiosResponse, AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
interface User {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
first_name: string;
|
|
7
|
+
last_name: string;
|
|
8
|
+
is_superuser: boolean;
|
|
9
|
+
is_active: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface UserRegister {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
first_name: string;
|
|
15
|
+
last_name: string;
|
|
16
|
+
}
|
|
17
|
+
interface NewUser extends UserRegister {
|
|
18
|
+
confirmPassword: string;
|
|
19
|
+
}
|
|
20
|
+
interface UpdatePasswordForm {
|
|
21
|
+
current_password: string;
|
|
22
|
+
new_password: string;
|
|
23
|
+
confirmNewPassword: string;
|
|
24
|
+
}
|
|
25
|
+
interface ReactiveRef<T> {
|
|
26
|
+
value: T;
|
|
27
|
+
set: (newValue: T) => void;
|
|
28
|
+
}
|
|
29
|
+
type ReactiveFactory = <T>(initial: T) => ReactiveRef<T>;
|
|
30
|
+
|
|
31
|
+
interface NewPasswordPayload {
|
|
32
|
+
token: string;
|
|
33
|
+
new_password: string;
|
|
34
|
+
}
|
|
35
|
+
declare function login(username: string, password: string): Promise<void>;
|
|
36
|
+
declare function logout(): void;
|
|
37
|
+
declare function passwordRecovery(email?: string): Promise<AxiosResponse>;
|
|
38
|
+
declare function resetPassword(newPassword: NewPasswordPayload['new_password']): Promise<AxiosResponse>;
|
|
39
|
+
declare function getCurrentUser(): Promise<AxiosResponse<User>>;
|
|
40
|
+
declare function signup(user: NewUser): Promise<AxiosResponse<User>>;
|
|
41
|
+
declare function updatePassword(form: UpdatePasswordForm): Promise<AxiosResponse>;
|
|
42
|
+
declare function updateUserProfile(user: Partial<User>): Promise<AxiosResponse<User>>;
|
|
43
|
+
declare function setUserStatus(userId: string, isActive: boolean): Promise<AxiosResponse>;
|
|
44
|
+
declare function deleteUser(userId: string): Promise<AxiosResponse>;
|
|
45
|
+
|
|
46
|
+
declare function initAuth({ axios, errorHandler, reactive }?: {
|
|
47
|
+
axios?: AxiosInstance;
|
|
48
|
+
errorHandler?: (error: any) => void;
|
|
49
|
+
reactive?: ReactiveFactory;
|
|
50
|
+
}): {
|
|
51
|
+
install(app: any): void;
|
|
52
|
+
useAuth: typeof useAuth;
|
|
53
|
+
};
|
|
54
|
+
declare function useAuth(): {
|
|
55
|
+
currentUser: ReactiveRef<User>;
|
|
56
|
+
passwordForm: ReactiveRef<UpdatePasswordForm>;
|
|
57
|
+
getFullName: () => string;
|
|
58
|
+
getIsLoggedIn: () => boolean;
|
|
59
|
+
logout: () => Promise<void>;
|
|
60
|
+
login: (credentials: {
|
|
61
|
+
email: string;
|
|
62
|
+
password: string;
|
|
63
|
+
}) => Promise<void>;
|
|
64
|
+
checkAuth: () => Promise<boolean>;
|
|
65
|
+
signup: (user: NewUser) => Promise<void>;
|
|
66
|
+
recoverPassword: (email: string) => Promise<void>;
|
|
67
|
+
resetPassword: (form: UpdatePasswordForm) => Promise<void>;
|
|
68
|
+
updatePassword: () => Promise<void>;
|
|
69
|
+
updateProfile: (user: Partial<User>) => Promise<void>;
|
|
70
|
+
toggleUserStatus: (userId: string, isActive: boolean) => Promise<void>;
|
|
71
|
+
deleteUser: (userId: string) => Promise<void>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export { type NewPasswordPayload, type NewUser, type ReactiveFactory, type ReactiveRef, type UpdatePasswordForm, type User, type UserRegister, deleteUser, getCurrentUser, initAuth, login, logout, passwordRecovery, resetPassword, setUserStatus, signup, updatePassword, updateUserProfile, useAuth };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { AxiosResponse, AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
interface User {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
first_name: string;
|
|
7
|
+
last_name: string;
|
|
8
|
+
is_superuser: boolean;
|
|
9
|
+
is_active: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface UserRegister {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
first_name: string;
|
|
15
|
+
last_name: string;
|
|
16
|
+
}
|
|
17
|
+
interface NewUser extends UserRegister {
|
|
18
|
+
confirmPassword: string;
|
|
19
|
+
}
|
|
20
|
+
interface UpdatePasswordForm {
|
|
21
|
+
current_password: string;
|
|
22
|
+
new_password: string;
|
|
23
|
+
confirmNewPassword: string;
|
|
24
|
+
}
|
|
25
|
+
interface ReactiveRef<T> {
|
|
26
|
+
value: T;
|
|
27
|
+
set: (newValue: T) => void;
|
|
28
|
+
}
|
|
29
|
+
type ReactiveFactory = <T>(initial: T) => ReactiveRef<T>;
|
|
30
|
+
|
|
31
|
+
interface NewPasswordPayload {
|
|
32
|
+
token: string;
|
|
33
|
+
new_password: string;
|
|
34
|
+
}
|
|
35
|
+
declare function login(username: string, password: string): Promise<void>;
|
|
36
|
+
declare function logout(): void;
|
|
37
|
+
declare function passwordRecovery(email?: string): Promise<AxiosResponse>;
|
|
38
|
+
declare function resetPassword(newPassword: NewPasswordPayload['new_password']): Promise<AxiosResponse>;
|
|
39
|
+
declare function getCurrentUser(): Promise<AxiosResponse<User>>;
|
|
40
|
+
declare function signup(user: NewUser): Promise<AxiosResponse<User>>;
|
|
41
|
+
declare function updatePassword(form: UpdatePasswordForm): Promise<AxiosResponse>;
|
|
42
|
+
declare function updateUserProfile(user: Partial<User>): Promise<AxiosResponse<User>>;
|
|
43
|
+
declare function setUserStatus(userId: string, isActive: boolean): Promise<AxiosResponse>;
|
|
44
|
+
declare function deleteUser(userId: string): Promise<AxiosResponse>;
|
|
45
|
+
|
|
46
|
+
declare function initAuth({ axios, errorHandler, reactive }?: {
|
|
47
|
+
axios?: AxiosInstance;
|
|
48
|
+
errorHandler?: (error: any) => void;
|
|
49
|
+
reactive?: ReactiveFactory;
|
|
50
|
+
}): {
|
|
51
|
+
install(app: any): void;
|
|
52
|
+
useAuth: typeof useAuth;
|
|
53
|
+
};
|
|
54
|
+
declare function useAuth(): {
|
|
55
|
+
currentUser: ReactiveRef<User>;
|
|
56
|
+
passwordForm: ReactiveRef<UpdatePasswordForm>;
|
|
57
|
+
getFullName: () => string;
|
|
58
|
+
getIsLoggedIn: () => boolean;
|
|
59
|
+
logout: () => Promise<void>;
|
|
60
|
+
login: (credentials: {
|
|
61
|
+
email: string;
|
|
62
|
+
password: string;
|
|
63
|
+
}) => Promise<void>;
|
|
64
|
+
checkAuth: () => Promise<boolean>;
|
|
65
|
+
signup: (user: NewUser) => Promise<void>;
|
|
66
|
+
recoverPassword: (email: string) => Promise<void>;
|
|
67
|
+
resetPassword: (form: UpdatePasswordForm) => Promise<void>;
|
|
68
|
+
updatePassword: () => Promise<void>;
|
|
69
|
+
updateProfile: (user: Partial<User>) => Promise<void>;
|
|
70
|
+
toggleUserStatus: (userId: string, isActive: boolean) => Promise<void>;
|
|
71
|
+
deleteUser: (userId: string) => Promise<void>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export { type NewPasswordPayload, type NewUser, type ReactiveFactory, type ReactiveRef, type UpdatePasswordForm, type User, type UserRegister, deleteUser, getCurrentUser, initAuth, login, logout, passwordRecovery, resetPassword, setUserStatus, signup, updatePassword, updateUserProfile, useAuth };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import 'axios';
|
|
2
|
+
|
|
3
|
+
let api$1 = null;
|
|
4
|
+
function getApi() {
|
|
5
|
+
if (!api$1) {
|
|
6
|
+
throw new Error("API not initialized. Call initApi first.");
|
|
7
|
+
}
|
|
8
|
+
return api$1;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ax = getApi();
|
|
12
|
+
ax.interceptors.request.use((config) => {
|
|
13
|
+
const token = localStorage.getItem("access_token");
|
|
14
|
+
if (token !== null && config.headers) {
|
|
15
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
16
|
+
}
|
|
17
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
18
|
+
const resetToken = urlParams.get("token");
|
|
19
|
+
if (resetToken !== null && config.headers) {
|
|
20
|
+
config.headers.Authorization = `Bearer ${resetToken}`;
|
|
21
|
+
}
|
|
22
|
+
return config;
|
|
23
|
+
});
|
|
24
|
+
async function login(username, password) {
|
|
25
|
+
const { data } = await ax.post("/auth/login", {
|
|
26
|
+
username: username.toLowerCase(),
|
|
27
|
+
password
|
|
28
|
+
});
|
|
29
|
+
localStorage.setItem("access_token", data.access_token);
|
|
30
|
+
}
|
|
31
|
+
function logout() {
|
|
32
|
+
localStorage.removeItem("access_token");
|
|
33
|
+
window.location.reload();
|
|
34
|
+
}
|
|
35
|
+
async function passwordRecovery(email) {
|
|
36
|
+
return ax.post(`/auth/password-recovery`, { email });
|
|
37
|
+
}
|
|
38
|
+
async function resetPassword(newPassword) {
|
|
39
|
+
return ax.post("/auth/reset-password", { new_password: newPassword });
|
|
40
|
+
}
|
|
41
|
+
async function getCurrentUser() {
|
|
42
|
+
return ax.get("/auth/me");
|
|
43
|
+
}
|
|
44
|
+
async function signup(user) {
|
|
45
|
+
return ax.post("/auth/signup", {
|
|
46
|
+
email: user.email.toLowerCase(),
|
|
47
|
+
password: user.password,
|
|
48
|
+
first_name: user.first_name,
|
|
49
|
+
last_name: user.last_name
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function updatePassword(form) {
|
|
53
|
+
return ax.put("/auth/password", {
|
|
54
|
+
current_password: form.current_password,
|
|
55
|
+
new_password: form.new_password
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async function updateUserProfile(user) {
|
|
59
|
+
return ax.put("/auth/profile", user);
|
|
60
|
+
}
|
|
61
|
+
async function setUserStatus(userId, isActive) {
|
|
62
|
+
return ax.patch(`/auth/users/${userId}/status`, { is_active: isActive });
|
|
63
|
+
}
|
|
64
|
+
async function deleteUser(userId) {
|
|
65
|
+
return ax.delete(`/auth/users/${userId}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let api = null;
|
|
69
|
+
let onError = null;
|
|
70
|
+
let createRef = null;
|
|
71
|
+
const defaultReactiveFactory = (initial) => {
|
|
72
|
+
let value = initial;
|
|
73
|
+
return {
|
|
74
|
+
get value() {
|
|
75
|
+
return value;
|
|
76
|
+
},
|
|
77
|
+
set: (newValue) => {
|
|
78
|
+
value = newValue;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
function createVueReactiveFactory(app) {
|
|
83
|
+
return (initial) => {
|
|
84
|
+
const refValue = app.ref(initial);
|
|
85
|
+
return {
|
|
86
|
+
get value() {
|
|
87
|
+
return refValue.value;
|
|
88
|
+
},
|
|
89
|
+
set: (v) => {
|
|
90
|
+
refValue.value = v;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function initAuth({
|
|
96
|
+
axios,
|
|
97
|
+
errorHandler,
|
|
98
|
+
reactive = defaultReactiveFactory
|
|
99
|
+
} = {}) {
|
|
100
|
+
api = axios || null;
|
|
101
|
+
onError = errorHandler || null;
|
|
102
|
+
createRef = reactive;
|
|
103
|
+
return {
|
|
104
|
+
install(app) {
|
|
105
|
+
if (app.ref) {
|
|
106
|
+
createRef = createVueReactiveFactory(app);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
useAuth
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function useAuth() {
|
|
113
|
+
if (!api || !createRef) {
|
|
114
|
+
throw new Error("Auth composable not initialized. Call initAuth first.");
|
|
115
|
+
}
|
|
116
|
+
const currentUser = createRef({
|
|
117
|
+
id: "",
|
|
118
|
+
email: "",
|
|
119
|
+
first_name: "",
|
|
120
|
+
last_name: "",
|
|
121
|
+
is_superuser: false,
|
|
122
|
+
is_active: false
|
|
123
|
+
});
|
|
124
|
+
const passwordForm = createRef({
|
|
125
|
+
current_password: "",
|
|
126
|
+
new_password: "",
|
|
127
|
+
confirmNewPassword: ""
|
|
128
|
+
});
|
|
129
|
+
const getFullName = () => `${currentUser.value.first_name} ${currentUser.value.last_name}`;
|
|
130
|
+
const getIsLoggedIn = () => currentUser.value.id.length > 0;
|
|
131
|
+
function handleError(error) {
|
|
132
|
+
if (onError) {
|
|
133
|
+
onError(error);
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
async function logout$1() {
|
|
138
|
+
try {
|
|
139
|
+
await logout();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
handleError(error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function login$1(credentials) {
|
|
145
|
+
const email = credentials.email.toLowerCase();
|
|
146
|
+
const { password } = credentials;
|
|
147
|
+
try {
|
|
148
|
+
await login(email, password);
|
|
149
|
+
await checkAuth();
|
|
150
|
+
} catch (error) {
|
|
151
|
+
handleError(error);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function checkAuth() {
|
|
155
|
+
try {
|
|
156
|
+
if (!getIsLoggedIn()) {
|
|
157
|
+
const { data } = await getCurrentUser();
|
|
158
|
+
currentUser.set(data);
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return getIsLoggedIn();
|
|
164
|
+
}
|
|
165
|
+
async function signup$1(user) {
|
|
166
|
+
try {
|
|
167
|
+
if (user.password !== user.confirmPassword) {
|
|
168
|
+
throw new Error("Passwords do not match");
|
|
169
|
+
}
|
|
170
|
+
const { data } = await signup(user);
|
|
171
|
+
currentUser.set(data);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
handleError(error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function recoverPassword(email) {
|
|
177
|
+
try {
|
|
178
|
+
await passwordRecovery(email);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
handleError(error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function resetPassword$1(form) {
|
|
184
|
+
try {
|
|
185
|
+
if (form.new_password !== form.confirmNewPassword) {
|
|
186
|
+
throw new Error("Passwords do not match");
|
|
187
|
+
}
|
|
188
|
+
await resetPassword(form.new_password);
|
|
189
|
+
form = {
|
|
190
|
+
current_password: "",
|
|
191
|
+
new_password: "",
|
|
192
|
+
confirmNewPassword: ""
|
|
193
|
+
};
|
|
194
|
+
} catch (error) {
|
|
195
|
+
handleError(error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async function updatePassword$1() {
|
|
199
|
+
try {
|
|
200
|
+
if (passwordForm.value.new_password !== passwordForm.value.confirmNewPassword) {
|
|
201
|
+
throw new Error("Passwords do not match");
|
|
202
|
+
}
|
|
203
|
+
await updatePassword(passwordForm.value);
|
|
204
|
+
passwordForm.set({
|
|
205
|
+
current_password: "",
|
|
206
|
+
new_password: "",
|
|
207
|
+
confirmNewPassword: ""
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
handleError(error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function updateProfile(user) {
|
|
214
|
+
try {
|
|
215
|
+
const { data } = await updateUserProfile(user);
|
|
216
|
+
currentUser.set({ ...currentUser.value, ...data });
|
|
217
|
+
} catch (error) {
|
|
218
|
+
handleError(error);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async function toggleUserStatus(userId, isActive) {
|
|
222
|
+
try {
|
|
223
|
+
await setUserStatus(userId, isActive);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
handleError(error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function deleteUser$1(userId) {
|
|
229
|
+
try {
|
|
230
|
+
await deleteUser(userId);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
handleError(error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
// State
|
|
237
|
+
currentUser,
|
|
238
|
+
passwordForm,
|
|
239
|
+
// Getters
|
|
240
|
+
getFullName,
|
|
241
|
+
getIsLoggedIn,
|
|
242
|
+
// Actions
|
|
243
|
+
logout: logout$1,
|
|
244
|
+
login: login$1,
|
|
245
|
+
checkAuth,
|
|
246
|
+
signup: signup$1,
|
|
247
|
+
recoverPassword,
|
|
248
|
+
resetPassword: resetPassword$1,
|
|
249
|
+
updatePassword: updatePassword$1,
|
|
250
|
+
updateProfile,
|
|
251
|
+
toggleUserStatus,
|
|
252
|
+
deleteUser: deleteUser$1
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export { deleteUser, getCurrentUser, initAuth, login, logout, passwordRecovery, resetPassword, setUserStatus, signup, updatePassword, updateUserProfile, useAuth };
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bagelink/auth",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.1.39",
|
|
5
|
+
"description": "Bagelink auth package",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Bagel Studio",
|
|
8
|
+
"email": "info@bagelstudio.co.il",
|
|
9
|
+
"url": "https://bagelstudio.co.il"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://github.com/bageldb/bagelink/tree/master/packages/auth#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/bageldb/bagelink.git",
|
|
16
|
+
"directory": "packages/auth"
|
|
17
|
+
},
|
|
18
|
+
"bugs": "https://github.com/bageldb/bagelink/issues",
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"require": "./dist/index.cjs",
|
|
25
|
+
"import": "./dist/index.mjs"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"main": "./dist/index.mjs",
|
|
29
|
+
"module": "./dist/index.mjs",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"src"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"axios": "^1.8.4"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^20.0.0",
|
|
43
|
+
"typescript": "^5.0.0",
|
|
44
|
+
"unbuild": "^2.0.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"dev": "unbuild --stub",
|
|
48
|
+
"build": "unbuild",
|
|
49
|
+
"start": "tsx src/index.ts",
|
|
50
|
+
"watch": "tsx watch src/index.ts"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/api/api.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios'
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
|
|
4
|
+
let api: AxiosInstance | null = null
|
|
5
|
+
|
|
6
|
+
export function initApi(axiosInstance?: AxiosInstance) {
|
|
7
|
+
if (axiosInstance) {
|
|
8
|
+
api = axiosInstance
|
|
9
|
+
} else {
|
|
10
|
+
api = axios.create({
|
|
11
|
+
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000',
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getApi(): AxiosInstance {
|
|
20
|
+
if (!api) {
|
|
21
|
+
throw new Error('API not initialized. Call initApi first.')
|
|
22
|
+
}
|
|
23
|
+
return api
|
|
24
|
+
}
|
package/src/api/auth.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AxiosResponse, InternalAxiosRequestConfig } from 'axios'
|
|
2
|
+
import type { User, NewUser, UpdatePasswordForm } from '../types'
|
|
3
|
+
import { getApi } from './api'
|
|
4
|
+
|
|
5
|
+
export interface NewPasswordPayload {
|
|
6
|
+
token: string
|
|
7
|
+
new_password: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ax = getApi()
|
|
11
|
+
|
|
12
|
+
ax.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
|
13
|
+
const token = localStorage.getItem('access_token')
|
|
14
|
+
if (token !== null && config.headers) {
|
|
15
|
+
config.headers.Authorization = `Bearer ${token}`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const urlParams = new URLSearchParams(window.location.search)
|
|
19
|
+
const resetToken = urlParams.get('token')
|
|
20
|
+
|
|
21
|
+
if (resetToken !== null && config.headers) {
|
|
22
|
+
config.headers.Authorization = `Bearer ${resetToken}`
|
|
23
|
+
}
|
|
24
|
+
return config
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export async function login(username: string, password: string) {
|
|
28
|
+
const { data } = await ax.post('/auth/login', {
|
|
29
|
+
username: username.toLowerCase(),
|
|
30
|
+
password,
|
|
31
|
+
})
|
|
32
|
+
localStorage.setItem('access_token', data.access_token)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function logout() {
|
|
36
|
+
localStorage.removeItem('access_token')
|
|
37
|
+
window.location.reload()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function passwordRecovery(email?: string): Promise<AxiosResponse> {
|
|
41
|
+
return ax.post(`/auth/password-recovery`, { email })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function resetPassword(
|
|
45
|
+
newPassword: NewPasswordPayload['new_password'],
|
|
46
|
+
): Promise<AxiosResponse> {
|
|
47
|
+
return ax.post('/auth/reset-password', { new_password: newPassword })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// User Management APIs
|
|
51
|
+
export async function getCurrentUser(): Promise<AxiosResponse<User>> {
|
|
52
|
+
return ax.get('/auth/me')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function signup(user: NewUser): Promise<AxiosResponse<User>> {
|
|
56
|
+
return ax.post('/auth/signup', {
|
|
57
|
+
email: user.email.toLowerCase(),
|
|
58
|
+
password: user.password,
|
|
59
|
+
first_name: user.first_name,
|
|
60
|
+
last_name: user.last_name,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function updatePassword(form: UpdatePasswordForm): Promise<AxiosResponse> {
|
|
65
|
+
return ax.put('/auth/password', {
|
|
66
|
+
current_password: form.current_password,
|
|
67
|
+
new_password: form.new_password,
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function updateUserProfile(user: Partial<User>): Promise<AxiosResponse<User>> {
|
|
72
|
+
return ax.put('/auth/profile', user)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function setUserStatus(userId: string, isActive: boolean): Promise<AxiosResponse> {
|
|
76
|
+
return ax.patch(`/auth/users/${userId}/status`, { is_active: isActive })
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function deleteUser(userId: string): Promise<AxiosResponse> {
|
|
80
|
+
return ax.delete(`/auth/users/${userId}`)
|
|
81
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios'
|
|
2
|
+
import type { User, NewUser, UpdatePasswordForm, ReactiveFactory } from '../types'
|
|
3
|
+
import * as authApi from '../api/auth'
|
|
4
|
+
|
|
5
|
+
let api: AxiosInstance | null = null
|
|
6
|
+
let onError: ((error: any) => void) | null = null
|
|
7
|
+
let createRef: ReactiveFactory | null = null
|
|
8
|
+
|
|
9
|
+
const defaultReactiveFactory: ReactiveFactory = <T>(initial: T) => {
|
|
10
|
+
let value = initial
|
|
11
|
+
return {
|
|
12
|
+
get value() { return value },
|
|
13
|
+
set: (newValue: T) => { value = newValue }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Vue plugin type
|
|
18
|
+
interface VuePlugin {
|
|
19
|
+
install: (app: any) => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Create Vue reactive factory
|
|
23
|
+
function createVueReactiveFactory(app: any): ReactiveFactory {
|
|
24
|
+
return <T>(initial: T) => {
|
|
25
|
+
const refValue = app.ref(initial)
|
|
26
|
+
return {
|
|
27
|
+
get value() { return refValue.value },
|
|
28
|
+
set: (v: T) => { refValue.value = v }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Create plugin instance
|
|
34
|
+
export function initAuth({
|
|
35
|
+
axios,
|
|
36
|
+
errorHandler,
|
|
37
|
+
reactive = defaultReactiveFactory
|
|
38
|
+
}: {
|
|
39
|
+
axios?: AxiosInstance
|
|
40
|
+
errorHandler?: (error: any) => void
|
|
41
|
+
reactive?: ReactiveFactory
|
|
42
|
+
} = {}) {
|
|
43
|
+
api = axios || null
|
|
44
|
+
onError = errorHandler || null
|
|
45
|
+
createRef = reactive
|
|
46
|
+
|
|
47
|
+
// Return plugin interface
|
|
48
|
+
return {
|
|
49
|
+
install(app: any) {
|
|
50
|
+
// If Vue is detected, use Vue's reactivity
|
|
51
|
+
if (app.ref) {
|
|
52
|
+
createRef = createVueReactiveFactory(app)
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
useAuth
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function useAuth() {
|
|
60
|
+
if (!api || !createRef) {
|
|
61
|
+
throw new Error('Auth composable not initialized. Call initAuth first.')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// State
|
|
65
|
+
const currentUser = createRef<User>({
|
|
66
|
+
id: '',
|
|
67
|
+
email: '',
|
|
68
|
+
first_name: '',
|
|
69
|
+
last_name: '',
|
|
70
|
+
is_superuser: false,
|
|
71
|
+
is_active: false,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const passwordForm = createRef<UpdatePasswordForm>({
|
|
75
|
+
current_password: '',
|
|
76
|
+
new_password: '',
|
|
77
|
+
confirmNewPassword: '',
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// Getters
|
|
81
|
+
const getFullName = () => `${currentUser.value.first_name} ${currentUser.value.last_name}`
|
|
82
|
+
const getIsLoggedIn = () => currentUser.value.id.length > 0
|
|
83
|
+
|
|
84
|
+
// Error handling
|
|
85
|
+
function handleError(error: any) {
|
|
86
|
+
if (onError) {
|
|
87
|
+
onError(error)
|
|
88
|
+
}
|
|
89
|
+
throw error
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Actions
|
|
93
|
+
async function logout() {
|
|
94
|
+
try {
|
|
95
|
+
await authApi.logout()
|
|
96
|
+
} catch (error: any) {
|
|
97
|
+
handleError(error)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function login(credentials: { email: string, password: string }) {
|
|
102
|
+
const email = credentials.email.toLowerCase()
|
|
103
|
+
const { password } = credentials
|
|
104
|
+
try {
|
|
105
|
+
await authApi.login(email, password)
|
|
106
|
+
await checkAuth()
|
|
107
|
+
} catch (error: any) {
|
|
108
|
+
handleError(error)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function checkAuth(): Promise<boolean> {
|
|
113
|
+
try {
|
|
114
|
+
if (!getIsLoggedIn()) {
|
|
115
|
+
const { data } = await authApi.getCurrentUser()
|
|
116
|
+
currentUser.set(data)
|
|
117
|
+
}
|
|
118
|
+
} catch (error: any) {
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
return getIsLoggedIn()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function signup(user: NewUser) {
|
|
125
|
+
try {
|
|
126
|
+
if (user.password !== user.confirmPassword) {
|
|
127
|
+
throw new Error('Passwords do not match')
|
|
128
|
+
}
|
|
129
|
+
const { data } = await authApi.signup(user)
|
|
130
|
+
currentUser.set(data)
|
|
131
|
+
} catch (error: any) {
|
|
132
|
+
handleError(error)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function recoverPassword(email: string) {
|
|
137
|
+
try {
|
|
138
|
+
await authApi.passwordRecovery(email)
|
|
139
|
+
} catch (error: any) {
|
|
140
|
+
handleError(error)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function resetPassword(form: UpdatePasswordForm) {
|
|
145
|
+
try {
|
|
146
|
+
if (form.new_password !== form.confirmNewPassword) {
|
|
147
|
+
throw new Error('Passwords do not match')
|
|
148
|
+
}
|
|
149
|
+
await authApi.resetPassword(form.new_password)
|
|
150
|
+
form = {
|
|
151
|
+
current_password: '',
|
|
152
|
+
new_password: '',
|
|
153
|
+
confirmNewPassword: '',
|
|
154
|
+
}
|
|
155
|
+
} catch (error: any) {
|
|
156
|
+
handleError(error)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function updatePassword() {
|
|
161
|
+
try {
|
|
162
|
+
if (passwordForm.value.new_password !== passwordForm.value.confirmNewPassword) {
|
|
163
|
+
throw new Error('Passwords do not match')
|
|
164
|
+
}
|
|
165
|
+
await authApi.updatePassword(passwordForm.value)
|
|
166
|
+
passwordForm.set({
|
|
167
|
+
current_password: '',
|
|
168
|
+
new_password: '',
|
|
169
|
+
confirmNewPassword: '',
|
|
170
|
+
})
|
|
171
|
+
} catch (error: any) {
|
|
172
|
+
handleError(error)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function updateProfile(user: Partial<User>) {
|
|
177
|
+
try {
|
|
178
|
+
const { data } = await authApi.updateUserProfile(user)
|
|
179
|
+
currentUser.set({ ...currentUser.value, ...data })
|
|
180
|
+
} catch (error: any) {
|
|
181
|
+
handleError(error)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function toggleUserStatus(userId: string, isActive: boolean) {
|
|
186
|
+
try {
|
|
187
|
+
await authApi.setUserStatus(userId, isActive)
|
|
188
|
+
} catch (error: any) {
|
|
189
|
+
handleError(error)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function deleteUser(userId: string) {
|
|
194
|
+
try {
|
|
195
|
+
await authApi.deleteUser(userId)
|
|
196
|
+
} catch (error: any) {
|
|
197
|
+
handleError(error)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
// State
|
|
203
|
+
currentUser,
|
|
204
|
+
passwordForm,
|
|
205
|
+
|
|
206
|
+
// Getters
|
|
207
|
+
getFullName,
|
|
208
|
+
getIsLoggedIn,
|
|
209
|
+
|
|
210
|
+
// Actions
|
|
211
|
+
logout,
|
|
212
|
+
login,
|
|
213
|
+
checkAuth,
|
|
214
|
+
signup,
|
|
215
|
+
recoverPassword,
|
|
216
|
+
resetPassword,
|
|
217
|
+
updatePassword,
|
|
218
|
+
updateProfile,
|
|
219
|
+
toggleUserStatus,
|
|
220
|
+
deleteUser,
|
|
221
|
+
}
|
|
222
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface User {
|
|
2
|
+
id: string
|
|
3
|
+
email: string
|
|
4
|
+
first_name: string
|
|
5
|
+
last_name: string
|
|
6
|
+
is_superuser: boolean
|
|
7
|
+
is_active: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface UserRegister {
|
|
11
|
+
email: string
|
|
12
|
+
password: string
|
|
13
|
+
first_name: string
|
|
14
|
+
last_name: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface NewUser extends UserRegister {
|
|
18
|
+
confirmPassword: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface UpdatePasswordForm {
|
|
22
|
+
current_password: string
|
|
23
|
+
new_password: string
|
|
24
|
+
confirmNewPassword: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ReactiveRef<T> {
|
|
28
|
+
value: T
|
|
29
|
+
set: (newValue: T) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ReactiveFactory = <T>(initial: T) => ReactiveRef<T>
|