nuxt-auth-kit 1.0.0

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.
@@ -0,0 +1,157 @@
1
+ <template>
2
+ <div class="profile-update">
3
+ <h2 class="text-2xl font-bold text-[#1a2e1a] mb-6">{{ title }}</h2>
4
+
5
+ <form @submit.prevent="handleSubmit" class="space-y-5">
6
+ <div v-if="successMsg" class="bg-green-50 border border-green-200 text-green-700 rounded-xl px-4 py-3 text-sm">
7
+ {{ successMsg }}
8
+ </div>
9
+ <div v-if="error" class="bg-red-50 border border-red-200 text-red-700 rounded-xl px-4 py-3 text-sm">
10
+ {{ error }}
11
+ </div>
12
+
13
+ <!-- Avatar -->
14
+ <div v-if="showAvatar" class="flex items-center gap-5">
15
+ <div class="relative">
16
+ <div class="w-20 h-20 rounded-full overflow-hidden bg-[#1B4332]/10 flex items-center justify-center">
17
+ <img v-if="avatarPreview" :src="avatarPreview" class="w-full h-full object-cover" alt="avatar" />
18
+ <span v-else class="text-2xl font-bold text-[#1B4332]">{{ initials }}</span>
19
+ </div>
20
+ <label class="absolute -bottom-1 -right-1 w-7 h-7 bg-[#1B4332] rounded-full flex items-center justify-center cursor-pointer hover:bg-[#163828] transition">
21
+ <svg class="w-3.5 h-3.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
22
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
23
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
24
+ </svg>
25
+ <input type="file" accept="image/*" class="hidden" @change="handleAvatarChange" />
26
+ </label>
27
+ </div>
28
+ <div>
29
+ <p class="font-medium text-[#1a2e1a]">{{ form.name || user?.name }}</p>
30
+ <p class="text-sm text-[#6b7c6b]">{{ user?.email }}</p>
31
+ </div>
32
+ </div>
33
+
34
+ <!-- Name -->
35
+ <div>
36
+ <label class="block text-sm font-medium text-[#1a2e1a] mb-1.5">Nom complet</label>
37
+ <div class="relative">
38
+ <span class="absolute left-4 top-1/2 -translate-y-1/2 text-[#8a9a8a]">
39
+ <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
40
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
41
+ </svg>
42
+ </span>
43
+ <input
44
+ v-model="form.name"
45
+ type="text"
46
+ :placeholder="user?.name || 'Votre nom'"
47
+ class="w-full bg-white border border-[#e0e0d8] rounded-2xl py-3 pl-11 pr-4 text-[#1a2e1a] placeholder-[#aab4aa] focus:outline-none focus:border-[#1B4332] focus:ring-2 focus:ring-[#1B4332]/20 transition"
48
+ />
49
+ </div>
50
+ <p v-if="fieldErrors.name" class="text-red-500 text-xs mt-1">{{ fieldErrors.name }}</p>
51
+ </div>
52
+
53
+ <!-- Email -->
54
+ <div>
55
+ <label class="block text-sm font-medium text-[#1a2e1a] mb-1.5">Email</label>
56
+ <div class="relative">
57
+ <span class="absolute left-4 top-1/2 -translate-y-1/2 text-[#8a9a8a]">
58
+ <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
59
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
60
+ </svg>
61
+ </span>
62
+ <input
63
+ v-model="form.email"
64
+ type="email"
65
+ :placeholder="user?.email || 'votre@email.com'"
66
+ class="w-full bg-white border border-[#e0e0d8] rounded-2xl py-3 pl-11 pr-4 text-[#1a2e1a] placeholder-[#aab4aa] focus:outline-none focus:border-[#1B4332] focus:ring-2 focus:ring-[#1B4332]/20 transition"
67
+ />
68
+ </div>
69
+ <p v-if="fieldErrors.email" class="text-red-500 text-xs mt-1">{{ fieldErrors.email }}</p>
70
+ </div>
71
+
72
+ <!-- Extra slots for additional fields -->
73
+ <slot name="extra-fields" :form="form" />
74
+
75
+ <button
76
+ type="submit"
77
+ :disabled="loading"
78
+ class="bg-[#1B4332] hover:bg-[#163828] text-[#D4FF6B] font-semibold py-3 px-8 rounded-2xl transition-colors disabled:opacity-60"
79
+ >
80
+ <span v-if="!loading">Enregistrer les modifications</span>
81
+ <span v-else class="flex items-center gap-2">
82
+ <svg class="animate-spin w-4 h-4" viewBox="0 0 24 24" fill="none">
83
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/>
84
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/>
85
+ </svg>
86
+ Enregistrement...
87
+ </span>
88
+ </button>
89
+ </form>
90
+ </div>
91
+ </template>
92
+
93
+ <script setup lang="ts">
94
+ import { ref, reactive, computed, watch } from 'vue'
95
+ import { useAuth } from '../../composables/useAuth'
96
+
97
+ withDefaults(defineProps<{
98
+ title?: string
99
+ showAvatar?: boolean
100
+ }>(), {
101
+ title: 'Informations du profil',
102
+ showAvatar: true
103
+ })
104
+
105
+ const emit = defineEmits<{ success: [user: any] }>()
106
+
107
+ const { user, updateProfile, loading } = useAuth()
108
+
109
+ const form = reactive({
110
+ name: user?.name || '',
111
+ email: user?.email || '',
112
+ avatar: null as File | null
113
+ })
114
+
115
+ const avatarPreview = ref<string | null>(user?.avatar as string || null)
116
+ const error = ref<string | null>(null)
117
+ const successMsg = ref<string | null>(null)
118
+ const fieldErrors = reactive<Record<string, string>>({})
119
+
120
+ const initials = computed(() => {
121
+ const name = form.name || user?.name || ''
122
+ return name.split(' ').map((n: string) => n[0]).join('').toUpperCase().slice(0, 2)
123
+ })
124
+
125
+ function handleAvatarChange(e: Event) {
126
+ const file = (e.target as HTMLInputElement).files?.[0]
127
+ if (!file) return
128
+ form.avatar = file
129
+ const reader = new FileReader()
130
+ reader.onload = (ev) => { avatarPreview.value = ev.target?.result as string }
131
+ reader.readAsDataURL(file)
132
+ }
133
+
134
+ async function handleSubmit() {
135
+ error.value = null
136
+ successMsg.value = null
137
+ Object.keys(fieldErrors).forEach(k => delete fieldErrors[k])
138
+
139
+ const data: any = {}
140
+ if (form.name) data.name = form.name
141
+ if (form.email) data.email = form.email
142
+ if (form.avatar) data.avatar = form.avatar
143
+
144
+ const result = await updateProfile(data)
145
+
146
+ if (result.success) {
147
+ successMsg.value = 'Profil mis à jour avec succès !'
148
+ emit('success', user)
149
+ } else if (result.error) {
150
+ if (result.error.errors) {
151
+ Object.entries(result.error.errors).forEach(([k, v]) => { fieldErrors[k] = v[0] })
152
+ } else {
153
+ error.value = result.error.message
154
+ }
155
+ }
156
+ }
157
+ </script>
@@ -0,0 +1,58 @@
1
+ import type { LoginCredentials, RegisterData, UpdateProfileData, UpdatePasswordData, ForgotPasswordData, ResetPasswordData, ApiError } from '../types/index.js';
2
+ export declare function useAuth(): {
3
+ user: any;
4
+ token: any;
5
+ loading: any;
6
+ isAuthenticated: any;
7
+ isGuest: any;
8
+ hasRole: any;
9
+ hasPermission: any;
10
+ login: (credentials: LoginCredentials) => Promise<{
11
+ success: boolean;
12
+ error?: undefined;
13
+ } | {
14
+ success: boolean;
15
+ error: ApiError;
16
+ }>;
17
+ register: (data: RegisterData) => Promise<{
18
+ success: boolean;
19
+ error?: undefined;
20
+ } | {
21
+ success: boolean;
22
+ error: ApiError;
23
+ }>;
24
+ logout: () => Promise<void>;
25
+ fetchUser: () => Promise<any>;
26
+ updateProfile: (data: UpdateProfileData) => Promise<{
27
+ success: boolean;
28
+ error?: undefined;
29
+ } | {
30
+ success: boolean;
31
+ error: ApiError;
32
+ }>;
33
+ updatePassword: (data: UpdatePasswordData) => Promise<{
34
+ success: boolean;
35
+ error?: undefined;
36
+ } | {
37
+ success: boolean;
38
+ error: ApiError;
39
+ }>;
40
+ forgotPassword: (data: ForgotPasswordData) => Promise<{
41
+ success: boolean;
42
+ message: string;
43
+ error?: undefined;
44
+ } | {
45
+ success: boolean;
46
+ error: ApiError;
47
+ message?: undefined;
48
+ }>;
49
+ resetPassword: (data: ResetPasswordData) => Promise<{
50
+ success: boolean;
51
+ message: string;
52
+ error?: undefined;
53
+ } | {
54
+ success: boolean;
55
+ error: ApiError;
56
+ message?: undefined;
57
+ }>;
58
+ };
@@ -0,0 +1,214 @@
1
+ import { useRuntimeConfig, navigateTo, useCookie } from "#app";
2
+ import { useAuthStore } from "../stores/auth.js";
3
+ export function useAuth() {
4
+ const store = useAuthStore();
5
+ const config = useRuntimeConfig();
6
+ const opts = config.public.nuxtAuthKit;
7
+ const apiBase = opts?.apiBase || "";
8
+ const endpoints = {
9
+ login: "/api/auth/login",
10
+ register: "/api/auth/register",
11
+ logout: "/api/auth/logout",
12
+ me: "/api/auth/me",
13
+ updateProfile: "/api/auth/profile",
14
+ updatePassword: "/api/auth/password",
15
+ forgotPassword: "/api/auth/forgot-password",
16
+ resetPassword: "/api/auth/reset-password",
17
+ ...opts?.endpoints || {}
18
+ };
19
+ const redirects = {
20
+ login: "/auth/login",
21
+ home: "/",
22
+ afterLogout: "/auth/login",
23
+ ...opts?.redirects || {}
24
+ };
25
+ const tokenCookieName = opts?.tokenCookieName || "auth_token";
26
+ const tokenCookie = useCookie(tokenCookieName, {
27
+ default: () => null,
28
+ secure: true,
29
+ sameSite: "lax",
30
+ maxAge: 60 * 60 * 24 * 7
31
+ // 7 days
32
+ });
33
+ async function apiFetch(path, options = {}) {
34
+ const headers = {
35
+ "Content-Type": "application/json",
36
+ "Accept": "application/json",
37
+ ...options.headers || {}
38
+ };
39
+ if (store.token) {
40
+ headers["Authorization"] = `Bearer ${store.token}`;
41
+ }
42
+ const response = await fetch(`${apiBase}${path}`, {
43
+ ...options,
44
+ headers
45
+ });
46
+ const data = await response.json();
47
+ if (!response.ok) {
48
+ const error = {
49
+ message: data.message || "Une erreur est survenue",
50
+ errors: data.errors
51
+ };
52
+ throw error;
53
+ }
54
+ return data;
55
+ }
56
+ function persistToken(token) {
57
+ store.setToken(token);
58
+ tokenCookie.value = token;
59
+ }
60
+ function clearToken() {
61
+ store.clearAuth();
62
+ tokenCookie.value = null;
63
+ }
64
+ async function login(credentials) {
65
+ store.setLoading(true);
66
+ try {
67
+ const data = await apiFetch(endpoints.login, {
68
+ method: "POST",
69
+ body: JSON.stringify(credentials)
70
+ });
71
+ persistToken(data.token);
72
+ store.setUser(data.user);
73
+ await navigateTo(redirects.home);
74
+ return { success: true };
75
+ } catch (error) {
76
+ return { success: false, error };
77
+ } finally {
78
+ store.setLoading(false);
79
+ }
80
+ }
81
+ async function register(data) {
82
+ store.setLoading(true);
83
+ try {
84
+ const response = await apiFetch(endpoints.register, {
85
+ method: "POST",
86
+ body: JSON.stringify(data)
87
+ });
88
+ persistToken(response.token);
89
+ store.setUser(response.user);
90
+ await navigateTo(redirects.home);
91
+ return { success: true };
92
+ } catch (error) {
93
+ return { success: false, error };
94
+ } finally {
95
+ store.setLoading(false);
96
+ }
97
+ }
98
+ async function logout() {
99
+ store.setLoading(true);
100
+ try {
101
+ await apiFetch(endpoints.logout, { method: "POST" });
102
+ } catch (_) {
103
+ } finally {
104
+ clearToken();
105
+ store.setLoading(false);
106
+ await navigateTo(redirects.afterLogout);
107
+ }
108
+ }
109
+ async function fetchUser() {
110
+ if (!store.token && !tokenCookie.value) return null;
111
+ if (!store.token && tokenCookie.value) {
112
+ store.setToken(tokenCookie.value);
113
+ }
114
+ try {
115
+ const data = await apiFetch(endpoints.me);
116
+ store.setUser(data.user || data);
117
+ return store.user;
118
+ } catch (_) {
119
+ clearToken();
120
+ return null;
121
+ }
122
+ }
123
+ async function updateProfile(data) {
124
+ store.setLoading(true);
125
+ try {
126
+ let body;
127
+ let headers = {};
128
+ if (data.avatar instanceof File) {
129
+ const formData = new FormData();
130
+ Object.entries(data).forEach(([k, v]) => {
131
+ if (v !== void 0 && v !== null) formData.append(k, v);
132
+ });
133
+ body = formData;
134
+ } else {
135
+ body = JSON.stringify(data);
136
+ headers["Content-Type"] = "application/json";
137
+ }
138
+ const response = await apiFetch(endpoints.updateProfile, {
139
+ method: "PUT",
140
+ body,
141
+ headers
142
+ });
143
+ store.setUser(response.user || response);
144
+ return { success: true };
145
+ } catch (error) {
146
+ return { success: false, error };
147
+ } finally {
148
+ store.setLoading(false);
149
+ }
150
+ }
151
+ async function updatePassword(data) {
152
+ store.setLoading(true);
153
+ try {
154
+ await apiFetch(endpoints.updatePassword, {
155
+ method: "PUT",
156
+ body: JSON.stringify(data)
157
+ });
158
+ return { success: true };
159
+ } catch (error) {
160
+ return { success: false, error };
161
+ } finally {
162
+ store.setLoading(false);
163
+ }
164
+ }
165
+ async function forgotPassword(data) {
166
+ store.setLoading(true);
167
+ try {
168
+ const response = await apiFetch(endpoints.forgotPassword, {
169
+ method: "POST",
170
+ body: JSON.stringify(data)
171
+ });
172
+ return { success: true, message: response.message };
173
+ } catch (error) {
174
+ return { success: false, error };
175
+ } finally {
176
+ store.setLoading(false);
177
+ }
178
+ }
179
+ async function resetPassword(data) {
180
+ store.setLoading(true);
181
+ try {
182
+ const response = await apiFetch(endpoints.resetPassword, {
183
+ method: "POST",
184
+ body: JSON.stringify(data)
185
+ });
186
+ return { success: true, message: response.message };
187
+ } catch (error) {
188
+ return { success: false, error };
189
+ } finally {
190
+ store.setLoading(false);
191
+ }
192
+ }
193
+ const { hasRole, hasPermission } = store;
194
+ return {
195
+ // State
196
+ user: store.user,
197
+ token: store.token,
198
+ loading: store.loading,
199
+ isAuthenticated: store.isAuthenticated,
200
+ isGuest: store.isGuest,
201
+ // RBAC
202
+ hasRole,
203
+ hasPermission,
204
+ // Actions
205
+ login,
206
+ register,
207
+ logout,
208
+ fetchUser,
209
+ updateProfile,
210
+ updatePassword,
211
+ forgotPassword,
212
+ resetPassword
213
+ };
214
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import { defineNuxtRouteMiddleware, navigateTo, useRuntimeConfig } from "#app";
2
+ import { useAuthStore } from "../stores/auth.js";
3
+ export default defineNuxtRouteMiddleware((to) => {
4
+ const store = useAuthStore();
5
+ const config = useRuntimeConfig();
6
+ const opts = config.public.nuxtAuthKit;
7
+ const loginPath = opts?.redirects?.login || "/auth/login";
8
+ if (!store.isAuthenticated) {
9
+ return navigateTo({
10
+ path: loginPath,
11
+ query: { redirect: to.fullPath }
12
+ });
13
+ }
14
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import { defineNuxtRouteMiddleware, navigateTo, useRuntimeConfig } from "#app";
2
+ import { useAuthStore } from "../stores/auth.js";
3
+ export default defineNuxtRouteMiddleware(() => {
4
+ const store = useAuthStore();
5
+ const config = useRuntimeConfig();
6
+ const opts = config.public.nuxtAuthKit;
7
+ const homePath = opts?.redirects?.home || "/";
8
+ if (store.isAuthenticated) {
9
+ return navigateTo(homePath);
10
+ }
11
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,18 @@
1
+ import { defineNuxtRouteMiddleware, navigateTo, useRuntimeConfig } from "#app";
2
+ import { useAuthStore } from "../stores/auth.js";
3
+ export default defineNuxtRouteMiddleware((to, _from) => {
4
+ const store = useAuthStore();
5
+ const config = useRuntimeConfig();
6
+ const opts = config.public.nuxtAuthKit;
7
+ const loginPath = opts?.redirects?.login || "/auth/login";
8
+ if (!store.isAuthenticated) {
9
+ return navigateTo(loginPath);
10
+ }
11
+ const requiredRoles = to.meta?.roles;
12
+ if (requiredRoles && requiredRoles.length > 0) {
13
+ const superAdmin = opts?.rbac?.superAdminRole || "super-admin";
14
+ if (!store.hasRole(superAdmin) && !store.hasRole(requiredRoles)) {
15
+ return navigateTo("/");
16
+ }
17
+ }
18
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,20 @@
1
+ import { defineNuxtPlugin, useCookie, useRuntimeConfig } from "#app";
2
+ import { useAuthStore } from "../stores/auth.js";
3
+ import { useAuth } from "../composables/useAuth.js";
4
+ export default defineNuxtPlugin(async (nuxtApp) => {
5
+ const store = useAuthStore();
6
+ const config = useRuntimeConfig();
7
+ const opts = config.public.nuxtAuthKit;
8
+ const tokenCookieName = opts?.tokenCookieName || "auth_token";
9
+ const tokenCookie = useCookie(tokenCookieName);
10
+ if (tokenCookie.value && !store.token) {
11
+ store.setToken(tokenCookie.value);
12
+ try {
13
+ const { fetchUser } = useAuth();
14
+ await fetchUser();
15
+ } catch (_) {
16
+ store.clearAuth();
17
+ tokenCookie.value = null;
18
+ }
19
+ }
20
+ });
@@ -0,0 +1,38 @@
1
+ import type { AuthUser } from '../types/index.js';
2
+ export declare const useAuthStore: import("pinia").StoreDefinition<"nuxt-auth-kit", Pick<{
3
+ user: import("vue").Ref<any, any>;
4
+ token: import("vue").Ref<string | null, string | null>;
5
+ loading: import("vue").Ref<boolean, boolean>;
6
+ isAuthenticated: import("vue").ComputedRef<boolean>;
7
+ isGuest: import("vue").ComputedRef<boolean>;
8
+ hasRole: (role: string | string[]) => boolean;
9
+ hasPermission: (permission: string | string[]) => boolean;
10
+ setUser: (userData: AuthUser) => void;
11
+ setToken: (tokenValue: string) => void;
12
+ clearAuth: () => void;
13
+ setLoading: (state: boolean) => void;
14
+ }, "user" | "token" | "loading">, Pick<{
15
+ user: import("vue").Ref<any, any>;
16
+ token: import("vue").Ref<string | null, string | null>;
17
+ loading: import("vue").Ref<boolean, boolean>;
18
+ isAuthenticated: import("vue").ComputedRef<boolean>;
19
+ isGuest: import("vue").ComputedRef<boolean>;
20
+ hasRole: (role: string | string[]) => boolean;
21
+ hasPermission: (permission: string | string[]) => boolean;
22
+ setUser: (userData: AuthUser) => void;
23
+ setToken: (tokenValue: string) => void;
24
+ clearAuth: () => void;
25
+ setLoading: (state: boolean) => void;
26
+ }, "isAuthenticated" | "isGuest">, Pick<{
27
+ user: import("vue").Ref<any, any>;
28
+ token: import("vue").Ref<string | null, string | null>;
29
+ loading: import("vue").Ref<boolean, boolean>;
30
+ isAuthenticated: import("vue").ComputedRef<boolean>;
31
+ isGuest: import("vue").ComputedRef<boolean>;
32
+ hasRole: (role: string | string[]) => boolean;
33
+ hasPermission: (permission: string | string[]) => boolean;
34
+ setUser: (userData: AuthUser) => void;
35
+ setToken: (tokenValue: string) => void;
36
+ clearAuth: () => void;
37
+ setLoading: (state: boolean) => void;
38
+ }, "hasRole" | "hasPermission" | "setUser" | "setToken" | "clearAuth" | "setLoading">>;
@@ -0,0 +1,47 @@
1
+ import { defineStore } from "pinia";
2
+ import { ref, computed } from "vue";
3
+ export const useAuthStore = defineStore("nuxt-auth-kit", () => {
4
+ const user = ref(null);
5
+ const token = ref(null);
6
+ const loading = ref(false);
7
+ const isAuthenticated = computed(() => !!token.value && !!user.value);
8
+ const isGuest = computed(() => !isAuthenticated.value);
9
+ function hasRole(role) {
10
+ if (!user.value?.roles) return false;
11
+ const roles = Array.isArray(role) ? role : [role];
12
+ return roles.some((r) => user.value.roles.includes(r));
13
+ }
14
+ function hasPermission(permission) {
15
+ if (!user.value?.permissions) return false;
16
+ const perms = Array.isArray(permission) ? permission : [permission];
17
+ return perms.some((p) => user.value.permissions.includes(p));
18
+ }
19
+ function setUser(userData) {
20
+ user.value = userData;
21
+ }
22
+ function setToken(tokenValue) {
23
+ token.value = tokenValue;
24
+ }
25
+ function clearAuth() {
26
+ user.value = null;
27
+ token.value = null;
28
+ }
29
+ function setLoading(state) {
30
+ loading.value = state;
31
+ }
32
+ return {
33
+ user,
34
+ token,
35
+ loading,
36
+ isAuthenticated,
37
+ isGuest,
38
+ hasRole,
39
+ hasPermission,
40
+ setUser,
41
+ setToken,
42
+ clearAuth,
43
+ setLoading
44
+ };
45
+ }, {
46
+ persist: false
47
+ });
@@ -0,0 +1,85 @@
1
+ export interface AuthUser {
2
+ id: number | string;
3
+ name: string;
4
+ email: string;
5
+ avatar?: string;
6
+ roles?: string[];
7
+ permissions?: string[];
8
+ email_verified_at?: string | null;
9
+ created_at?: string;
10
+ updated_at?: string;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface LoginCredentials {
14
+ email: string;
15
+ password: string;
16
+ remember?: boolean;
17
+ }
18
+ export interface RegisterData {
19
+ name: string;
20
+ email: string;
21
+ password: string;
22
+ password_confirmation: string;
23
+ role?: string;
24
+ [key: string]: unknown;
25
+ }
26
+ export interface UpdateProfileData {
27
+ name?: string;
28
+ email?: string;
29
+ avatar?: File | string | null;
30
+ [key: string]: unknown;
31
+ }
32
+ export interface UpdatePasswordData {
33
+ current_password: string;
34
+ password: string;
35
+ password_confirmation: string;
36
+ }
37
+ export interface ForgotPasswordData {
38
+ email: string;
39
+ }
40
+ export interface ResetPasswordData {
41
+ token: string;
42
+ email: string;
43
+ password: string;
44
+ password_confirmation: string;
45
+ }
46
+ export interface AuthResponse {
47
+ user: AuthUser;
48
+ token: string;
49
+ token_type?: string;
50
+ expires_in?: number;
51
+ }
52
+ export interface ApiError {
53
+ message: string;
54
+ errors?: Record<string, string[]>;
55
+ }
56
+ export interface ModuleOptions {
57
+ /** Laravel API base URL */
58
+ apiBase: string;
59
+ /** API endpoints (customizable) */
60
+ endpoints?: {
61
+ login?: string;
62
+ register?: string;
63
+ logout?: string;
64
+ me?: string;
65
+ updateProfile?: string;
66
+ updatePassword?: string;
67
+ forgotPassword?: string;
68
+ resetPassword?: string;
69
+ };
70
+ /** Token storage strategy */
71
+ tokenStorage?: 'cookie' | 'localStorage';
72
+ /** Cookie name for token */
73
+ tokenCookieName?: string;
74
+ /** Redirect routes */
75
+ redirects?: {
76
+ login?: string;
77
+ home?: string;
78
+ afterLogout?: string;
79
+ };
80
+ /** Role-based access control */
81
+ rbac?: {
82
+ superAdminRole?: string;
83
+ defaultUserRole?: string;
84
+ };
85
+ }
File without changes