nuxt-users 1.10.0 → 1.12.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.
- package/CHANGELOG.md +58 -0
- package/dist/cli.mjs +2 -3
- package/dist/module.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +55 -30
- package/dist/runtime/assets/nuxt-users.css +1 -0
- package/dist/runtime/components/NUsersList.vue +136 -0
- package/dist/runtime/components/NUsersList.vue.d.ts +74 -0
- package/dist/runtime/components/{LoginForm.vue → NUsersLoginForm.vue} +20 -22
- package/dist/runtime/components/{LoginForm.vue.d.ts → NUsersLoginForm.vue.d.ts} +5 -9
- package/dist/runtime/components/{LogoutLink.vue → NUsersLogoutLink.vue} +2 -6
- package/dist/runtime/components/{PasswordStrengthIndicator.vue → NUsersPasswordStrengthIndicator.vue} +18 -22
- package/dist/runtime/components/{ProfileInfo.vue → NUsersProfileInfo.vue} +5 -7
- package/dist/runtime/components/{ResetPasswordForm.vue → NUsersResetPasswordForm.vue} +25 -28
- package/dist/runtime/components/{ResetPasswordForm.vue.d.ts → NUsersResetPasswordForm.vue.d.ts} +3 -6
- package/dist/runtime/components/NUsersUserCard.vue +93 -0
- package/dist/runtime/components/NUsersUserCard.vue.d.ts +33 -0
- package/dist/runtime/components/NUsersUserForm.vue +141 -0
- package/dist/runtime/components/NUsersUserForm.vue.d.ts +12 -0
- package/dist/runtime/composables/useAuthentication.d.ts +2 -0
- package/dist/runtime/composables/useAuthentication.js +5 -7
- package/dist/runtime/composables/usePasswordValidation.d.ts +2 -2
- package/dist/runtime/constants.d.ts +1 -0
- package/dist/runtime/constants.js +2 -1
- package/dist/runtime/middleware/authorization.client.js +6 -1
- package/dist/runtime/server/api/nuxt-users/index.get.d.ts +20 -0
- package/dist/runtime/server/api/nuxt-users/index.get.js +58 -0
- package/dist/runtime/server/api/{auth/forgot-password.post.js → nuxt-users/password/forgot.post.js} +1 -1
- package/dist/runtime/server/api/{auth/update-password.post.js → nuxt-users/password/index.patch.js} +2 -2
- package/dist/runtime/server/api/{auth/reset-password.post.js → nuxt-users/password/reset.post.js} +2 -2
- package/dist/runtime/server/api/{auth/logout.get.js → nuxt-users/session/index.delete.js} +1 -1
- package/dist/runtime/server/api/{auth/login.post.d.ts → nuxt-users/session/index.post.d.ts} +1 -0
- package/dist/runtime/server/api/{auth/login.post.js → nuxt-users/session/index.post.js} +1 -1
- package/dist/runtime/server/middleware/authorization.server.js +29 -9
- package/dist/runtime/server/utils/user.js +8 -5
- package/dist/shared/{nuxt-users.CXezgQLO.d.mts → nuxt-users.B5ttEpIO.d.mts} +8 -1
- package/dist/shared/{nuxt-users.CXezgQLO.d.ts → nuxt-users.B5ttEpIO.d.ts} +8 -1
- package/dist/utils.d.mts +1 -2
- package/dist/utils.d.ts +1 -2
- package/dist/utils.mjs +162 -2
- package/package.json +2 -2
- package/dist/runtime/assets/colors.css +0 -1
- package/dist/shared/nuxt-users.BnOQs2LI.mjs +0 -162
- package/dist/runtime/components/{LogoutLink.vue.d.ts → NUsersLogoutLink.vue.d.ts} +4 -4
- /package/dist/runtime/components/{PasswordStrengthIndicator.vue.d.ts → NUsersPasswordStrengthIndicator.vue.d.ts} +0 -0
- /package/dist/runtime/components/{ProfileInfo.vue.d.ts → NUsersProfileInfo.vue.d.ts} +0 -0
- /package/dist/runtime/server/api/nuxt-users/{profile.get.d.ts → me.get.d.ts} +0 -0
- /package/dist/runtime/server/api/nuxt-users/{profile.get.js → me.get.js} +0 -0
- /package/dist/runtime/server/api/{auth/forgot-password.post.d.ts → nuxt-users/password/forgot.post.d.ts} +0 -0
- /package/dist/runtime/server/api/{auth/logout.get.d.ts → nuxt-users/password/index.patch.d.ts} +0 -0
- /package/dist/runtime/server/api/{auth/reset-password.post.d.ts → nuxt-users/password/reset.post.d.ts} +0 -0
- /package/dist/runtime/server/api/{auth/update-password.post.d.ts → nuxt-users/session/index.delete.d.ts} +0 -0
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export const NO_AUTH_PATHS = ["/login"
|
|
1
|
+
export const NO_AUTH_PATHS = ["/login"];
|
|
2
|
+
export const NO_AUTH_API_PATHS = ["/session", "/password/forgot", "/password/reset"];
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { defineNuxtRouteMiddleware, navigateTo, useRuntimeConfig } from "#app";
|
|
2
2
|
import { useAuthentication } from "../composables/useAuthentication.js";
|
|
3
3
|
import { hasPermission, isWhitelisted } from "../utils/permissions.js";
|
|
4
|
-
import { NO_AUTH_PATHS } from "../constants.js";
|
|
4
|
+
import { NO_AUTH_PATHS, NO_AUTH_API_PATHS } from "../constants.js";
|
|
5
5
|
export default defineNuxtRouteMiddleware((to) => {
|
|
6
6
|
const { public: { nuxtUsers: publicNuxtUsers } } = useRuntimeConfig();
|
|
7
7
|
const publicOptions = publicNuxtUsers;
|
|
8
|
+
const base = publicOptions.apiBasePath || "/api/nuxt-users";
|
|
8
9
|
if (NO_AUTH_PATHS.includes(to.path)) {
|
|
9
10
|
console.log(`[Nuxt Users] client.middleware.auth.global: ${to.path}`);
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
13
|
+
const openApiPaths = NO_AUTH_API_PATHS.map((path) => `${base}${path}`);
|
|
14
|
+
if (openApiPaths.includes(to.path)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
12
17
|
if (isWhitelisted(to.path, publicOptions.auth.whitelist)) {
|
|
13
18
|
console.log(`[Nuxt Users] client.middleware.auth.global: Whitelisted: ${to.path}`);
|
|
14
19
|
return;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
2
|
+
users: {
|
|
3
|
+
id: number;
|
|
4
|
+
email: string;
|
|
5
|
+
name: string;
|
|
6
|
+
role: string;
|
|
7
|
+
created_at: string;
|
|
8
|
+
updated_at: string;
|
|
9
|
+
last_login_at: string | null;
|
|
10
|
+
}[];
|
|
11
|
+
pagination: {
|
|
12
|
+
page: number;
|
|
13
|
+
limit: number;
|
|
14
|
+
total: number;
|
|
15
|
+
totalPages: number;
|
|
16
|
+
hasNext: boolean;
|
|
17
|
+
hasPrev: boolean;
|
|
18
|
+
};
|
|
19
|
+
}>>;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getQuery } from "h3";
|
|
2
|
+
import { useRuntimeConfig } from "#imports";
|
|
3
|
+
import { useDb } from "../../utils/db.js";
|
|
4
|
+
import { getLastLoginTime } from "../../utils/user.js";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const { nuxtUsers } = useRuntimeConfig();
|
|
7
|
+
const options = nuxtUsers;
|
|
8
|
+
const db = await useDb(options);
|
|
9
|
+
const usersTable = options.tables.users;
|
|
10
|
+
const query = getQuery(event);
|
|
11
|
+
const page = Number(query.page) || 1;
|
|
12
|
+
const limit = Number(query.limit) || 10;
|
|
13
|
+
const offset = (page - 1) * limit;
|
|
14
|
+
if (page < 1 || limit < 1 || limit > 100) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 400,
|
|
17
|
+
statusMessage: "Invalid pagination parameters. Page must be >= 1, limit must be between 1 and 100."
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const countResult = await db.sql`SELECT COUNT(*) as total FROM {${usersTable}}`;
|
|
22
|
+
const total = countResult.rows[0].total;
|
|
23
|
+
const usersResult = await db.sql`
|
|
24
|
+
SELECT id, email, name, role, created_at, updated_at
|
|
25
|
+
FROM {${usersTable}}
|
|
26
|
+
ORDER BY name ASC
|
|
27
|
+
LIMIT ${limit} OFFSET ${offset}
|
|
28
|
+
`;
|
|
29
|
+
const users = await Promise.all(usersResult.rows.map(async (user) => {
|
|
30
|
+
const lastLoginTime = await getLastLoginTime(user.id, options);
|
|
31
|
+
return {
|
|
32
|
+
id: user.id,
|
|
33
|
+
email: user.email,
|
|
34
|
+
name: user.name,
|
|
35
|
+
role: user.role,
|
|
36
|
+
created_at: user.created_at instanceof Date ? user.created_at.toISOString() : user.created_at,
|
|
37
|
+
updated_at: user.updated_at instanceof Date ? user.updated_at.toISOString() : user.updated_at,
|
|
38
|
+
last_login_at: lastLoginTime
|
|
39
|
+
};
|
|
40
|
+
}));
|
|
41
|
+
return {
|
|
42
|
+
users,
|
|
43
|
+
pagination: {
|
|
44
|
+
page,
|
|
45
|
+
limit,
|
|
46
|
+
total,
|
|
47
|
+
totalPages: Math.ceil(total / limit),
|
|
48
|
+
hasNext: page * limit < total,
|
|
49
|
+
hasPrev: page > 1
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw createError({
|
|
54
|
+
statusCode: 500,
|
|
55
|
+
statusMessage: `Error fetching users: ${error instanceof Error ? error.message : String(error)}`
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
package/dist/runtime/server/api/{auth/forgot-password.post.js → nuxt-users/password/forgot.post.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineEventHandler, readBody, createError } from "h3";
|
|
2
|
-
import { sendPasswordResetLink } from "
|
|
2
|
+
import { sendPasswordResetLink } from "../../../services/password.js";
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
4
|
const body = await readBody(event);
|
|
5
5
|
const { email } = body;
|
package/dist/runtime/server/api/{auth/update-password.post.js → nuxt-users/password/index.patch.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getCookie, readBody } from "h3";
|
|
2
2
|
import bcrypt from "bcrypt";
|
|
3
3
|
import { useRuntimeConfig } from "#imports";
|
|
4
|
-
import { getCurrentUserFromToken, updateUserPassword } from "
|
|
5
|
-
import { validatePassword, getPasswordValidationOptions } from "
|
|
4
|
+
import { getCurrentUserFromToken, updateUserPassword } from "../../../utils/index.js";
|
|
5
|
+
import { validatePassword, getPasswordValidationOptions } from "../../../../../utils.js";
|
|
6
6
|
export default defineEventHandler(async (event) => {
|
|
7
7
|
const { nuxtUsers } = useRuntimeConfig();
|
|
8
8
|
const options = nuxtUsers;
|
package/dist/runtime/server/api/{auth/reset-password.post.js → nuxt-users/password/reset.post.js}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineEventHandler, readBody, createError, H3Error } from "h3";
|
|
2
|
-
import { resetPassword } from "
|
|
2
|
+
import { resetPassword } from "../../../services/password.js";
|
|
3
3
|
import { useRuntimeConfig } from "#imports";
|
|
4
|
-
import { validatePassword, getPasswordValidationOptions } from "
|
|
4
|
+
import { validatePassword, getPasswordValidationOptions } from "../../../../../utils.js";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
6
6
|
const body = await readBody(event);
|
|
7
7
|
const { token, email, password, password_confirmation } = body;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineEventHandler, getCookie, setCookie } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { useDb } from "
|
|
3
|
+
import { useDb } from "../../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
6
6
|
const options = nuxtUsers;
|
|
@@ -2,7 +2,7 @@ import { createError, defineEventHandler, readBody, setCookie } from "h3";
|
|
|
2
2
|
import bcrypt from "bcrypt";
|
|
3
3
|
import crypto from "node:crypto";
|
|
4
4
|
import { useRuntimeConfig } from "#imports";
|
|
5
|
-
import { useDb } from "
|
|
5
|
+
import { useDb } from "../../../utils/index.js";
|
|
6
6
|
export default defineEventHandler(async (event) => {
|
|
7
7
|
const body = await readBody(event);
|
|
8
8
|
const { email, password } = body;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
import { defineEventHandler, getCookie,
|
|
1
|
+
import { defineEventHandler, getCookie, createError } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
3
|
import { getCurrentUserFromToken } from "../utils/index.js";
|
|
4
4
|
import { hasPermission, isWhitelisted } from "../../utils/permissions.js";
|
|
5
|
-
import { NO_AUTH_PATHS } from "../../constants.js";
|
|
5
|
+
import { NO_AUTH_PATHS, NO_AUTH_API_PATHS } from "../../constants.js";
|
|
6
6
|
export default defineEventHandler(async (event) => {
|
|
7
7
|
const { nuxtUsers } = useRuntimeConfig();
|
|
8
8
|
const options = nuxtUsers;
|
|
9
|
+
const base = options.apiBasePath || "/api/nuxt-users";
|
|
9
10
|
const isPageOrApiRoute = !event.path.includes(".") && (event.path === "/" || event.path.startsWith("/api/") || !event.path.startsWith("/_"));
|
|
10
11
|
if (!isPageOrApiRoute) {
|
|
11
12
|
return;
|
|
12
13
|
}
|
|
13
14
|
if (NO_AUTH_PATHS.includes(event.path)) {
|
|
14
|
-
console.log(
|
|
15
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: NO_AUTH_PATH: ${event.path}`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const openApiPaths = NO_AUTH_API_PATHS.map((path) => `${base}${path}`);
|
|
19
|
+
if (openApiPaths.includes(event.path)) {
|
|
15
20
|
return;
|
|
16
21
|
}
|
|
17
22
|
if (isWhitelisted(event.path, options.auth.whitelist)) {
|
|
@@ -20,17 +25,32 @@ export default defineEventHandler(async (event) => {
|
|
|
20
25
|
}
|
|
21
26
|
const token = getCookie(event, "auth_token");
|
|
22
27
|
if (!token) {
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
if (event.path.startsWith("/api/")) {
|
|
29
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} No token found - API request rejected`);
|
|
30
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
31
|
+
} else {
|
|
32
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} No token found - letting client handle page redirect`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
25
35
|
}
|
|
26
36
|
const user = await getCurrentUserFromToken(token, options);
|
|
27
37
|
if (!user) {
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
if (event.path.startsWith("/api/")) {
|
|
39
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} Invalid token - API request rejected`);
|
|
40
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
41
|
+
} else {
|
|
42
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} Invalid token - letting client handle page redirect`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
30
45
|
}
|
|
31
46
|
if (!hasPermission(user.role, event.path, options.auth.permissions)) {
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
if (event.path.startsWith("/api/")) {
|
|
48
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} User ${user.id} with role ${user.role} denied access - API request rejected`);
|
|
49
|
+
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
50
|
+
} else {
|
|
51
|
+
console.log(`[Nuxt Users] server.middleware.auth.global: ${event.path} User ${user.id} with role ${user.role} denied access - letting client handle page redirect`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
34
54
|
}
|
|
35
55
|
console.log(`[Nuxt Users] server.middleware.auth.global: Authenticated request to ${event.path} for ${user.id} with role ${user.role}`);
|
|
36
56
|
return;
|
|
@@ -69,21 +69,24 @@ export const findUserById = async (id, options, withPass = false) => {
|
|
|
69
69
|
export const updateUser = async (id, userData, options) => {
|
|
70
70
|
const db = await useDb(options);
|
|
71
71
|
const usersTable = options.tables.users;
|
|
72
|
-
const currentUser = await findUserById(id, options
|
|
72
|
+
const currentUser = await findUserById(id, options);
|
|
73
73
|
if (!currentUser) {
|
|
74
74
|
throw new Error("User not found.");
|
|
75
75
|
}
|
|
76
76
|
const fieldsToUpdate = { ...userData };
|
|
77
77
|
delete fieldsToUpdate.id;
|
|
78
|
+
delete fieldsToUpdate.created_at;
|
|
79
|
+
delete fieldsToUpdate.updated_at;
|
|
80
|
+
delete fieldsToUpdate.last_login_at;
|
|
78
81
|
delete fieldsToUpdate.password;
|
|
79
82
|
if (Object.keys(fieldsToUpdate).length === 0) {
|
|
80
|
-
|
|
81
|
-
return user;
|
|
83
|
+
return currentUser;
|
|
82
84
|
}
|
|
83
|
-
|
|
85
|
+
const dirtyFields = Object.keys(fieldsToUpdate).filter((key) => currentUser[key] != fieldsToUpdate[key]);
|
|
86
|
+
for (const key of dirtyFields) {
|
|
84
87
|
await db.sql`
|
|
85
88
|
UPDATE {${usersTable}}
|
|
86
|
-
SET ${key} = ${
|
|
89
|
+
SET {${key}} = ${fieldsToUpdate[key]}, updated_at = CURRENT_TIMESTAMP
|
|
87
90
|
WHERE id = ${id}
|
|
88
91
|
`;
|
|
89
92
|
}
|
|
@@ -12,6 +12,11 @@ interface RuntimeModuleOptions {
|
|
|
12
12
|
name: DatabaseType;
|
|
13
13
|
options: DatabaseConfig;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Base path for all module API endpoints
|
|
17
|
+
* @default '/api/nuxt-users'
|
|
18
|
+
*/
|
|
19
|
+
apiBasePath?: string;
|
|
15
20
|
tables?: {
|
|
16
21
|
migrations?: string;
|
|
17
22
|
users?: string;
|
|
@@ -49,7 +54,7 @@ interface RuntimeModuleOptions {
|
|
|
49
54
|
* @default {}
|
|
50
55
|
* @example {
|
|
51
56
|
* admin: ['*'], // admin can access everything
|
|
52
|
-
* user: ['/profile', '/api/
|
|
57
|
+
* user: ['/profile', '/api/nuxt-users/me'],
|
|
53
58
|
* moderator: ['/admin/*', '/api/admin/*']
|
|
54
59
|
* }
|
|
55
60
|
*/
|
|
@@ -96,6 +101,7 @@ interface ModuleOptions {
|
|
|
96
101
|
name: DatabaseType;
|
|
97
102
|
options: DatabaseConfig;
|
|
98
103
|
};
|
|
104
|
+
apiBasePath: string;
|
|
99
105
|
tables: {
|
|
100
106
|
migrations: string;
|
|
101
107
|
users: string;
|
|
@@ -138,6 +144,7 @@ interface User {
|
|
|
138
144
|
role: string;
|
|
139
145
|
created_at: string;
|
|
140
146
|
updated_at: string;
|
|
147
|
+
last_login_at?: string;
|
|
141
148
|
}
|
|
142
149
|
type UserWithoutPassword = Omit<User, 'password'>;
|
|
143
150
|
interface PersonalAccessToken {
|
|
@@ -12,6 +12,11 @@ interface RuntimeModuleOptions {
|
|
|
12
12
|
name: DatabaseType;
|
|
13
13
|
options: DatabaseConfig;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Base path for all module API endpoints
|
|
17
|
+
* @default '/api/nuxt-users'
|
|
18
|
+
*/
|
|
19
|
+
apiBasePath?: string;
|
|
15
20
|
tables?: {
|
|
16
21
|
migrations?: string;
|
|
17
22
|
users?: string;
|
|
@@ -49,7 +54,7 @@ interface RuntimeModuleOptions {
|
|
|
49
54
|
* @default {}
|
|
50
55
|
* @example {
|
|
51
56
|
* admin: ['*'], // admin can access everything
|
|
52
|
-
* user: ['/profile', '/api/
|
|
57
|
+
* user: ['/profile', '/api/nuxt-users/me'],
|
|
53
58
|
* moderator: ['/admin/*', '/api/admin/*']
|
|
54
59
|
* }
|
|
55
60
|
*/
|
|
@@ -96,6 +101,7 @@ interface ModuleOptions {
|
|
|
96
101
|
name: DatabaseType;
|
|
97
102
|
options: DatabaseConfig;
|
|
98
103
|
};
|
|
104
|
+
apiBasePath: string;
|
|
99
105
|
tables: {
|
|
100
106
|
migrations: string;
|
|
101
107
|
users: string;
|
|
@@ -138,6 +144,7 @@ interface User {
|
|
|
138
144
|
role: string;
|
|
139
145
|
created_at: string;
|
|
140
146
|
updated_at: string;
|
|
147
|
+
last_login_at?: string;
|
|
141
148
|
}
|
|
142
149
|
type UserWithoutPassword = Omit<User, 'password'>;
|
|
143
150
|
interface PersonalAccessToken {
|
package/dist/utils.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.
|
|
2
|
-
export { findUserByEmail, getCurrentUserFromToken, getLastLoginTime } from '../dist/runtime/server/utils/user.js';
|
|
1
|
+
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.B5ttEpIO.mjs';
|
|
3
2
|
|
|
4
3
|
interface PasswordValidationResult {
|
|
5
4
|
isValid: boolean;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.
|
|
2
|
-
export { findUserByEmail, getCurrentUserFromToken, getLastLoginTime } from '../dist/runtime/server/utils/user.js';
|
|
1
|
+
export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.B5ttEpIO.js';
|
|
3
2
|
|
|
4
3
|
interface PasswordValidationResult {
|
|
5
4
|
isValid: boolean;
|
package/dist/utils.mjs
CHANGED
|
@@ -1,2 +1,162 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const DEFAULT_OPTIONS = {
|
|
2
|
+
minLength: 8,
|
|
3
|
+
requireUppercase: true,
|
|
4
|
+
requireLowercase: true,
|
|
5
|
+
requireNumbers: true,
|
|
6
|
+
requireSpecialChars: true,
|
|
7
|
+
preventCommonPasswords: true
|
|
8
|
+
};
|
|
9
|
+
const getPasswordValidationOptions = (moduleOptions) => {
|
|
10
|
+
if (!moduleOptions?.passwordValidation) {
|
|
11
|
+
return DEFAULT_OPTIONS;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
...DEFAULT_OPTIONS,
|
|
15
|
+
...moduleOptions.passwordValidation
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
const COMMON_PASSWORDS = [
|
|
19
|
+
"password",
|
|
20
|
+
"123456",
|
|
21
|
+
"123456789",
|
|
22
|
+
"qwerty",
|
|
23
|
+
"abc123",
|
|
24
|
+
"password123",
|
|
25
|
+
"admin",
|
|
26
|
+
"letmein",
|
|
27
|
+
"welcome",
|
|
28
|
+
"monkey",
|
|
29
|
+
"dragon",
|
|
30
|
+
"master",
|
|
31
|
+
"hello",
|
|
32
|
+
"freedom",
|
|
33
|
+
"whatever",
|
|
34
|
+
"qazwsx",
|
|
35
|
+
"trustno1",
|
|
36
|
+
"jordan",
|
|
37
|
+
"harley",
|
|
38
|
+
"ranger",
|
|
39
|
+
"buster",
|
|
40
|
+
"thomas",
|
|
41
|
+
"tigger",
|
|
42
|
+
"robert",
|
|
43
|
+
"soccer",
|
|
44
|
+
"batman",
|
|
45
|
+
"test",
|
|
46
|
+
"pass",
|
|
47
|
+
"user",
|
|
48
|
+
"guest",
|
|
49
|
+
"login",
|
|
50
|
+
"secret",
|
|
51
|
+
"god",
|
|
52
|
+
"love",
|
|
53
|
+
"sex",
|
|
54
|
+
"money",
|
|
55
|
+
"password1",
|
|
56
|
+
"12345678",
|
|
57
|
+
"qwerty123",
|
|
58
|
+
"admin123",
|
|
59
|
+
"password!",
|
|
60
|
+
"password1!",
|
|
61
|
+
"password123",
|
|
62
|
+
"password123!"
|
|
63
|
+
];
|
|
64
|
+
const validatePassword = (password, options = {}) => {
|
|
65
|
+
const config = { ...DEFAULT_OPTIONS, ...options };
|
|
66
|
+
const errors = [];
|
|
67
|
+
const hints = [];
|
|
68
|
+
let score = 0;
|
|
69
|
+
if (password.length < config.minLength) {
|
|
70
|
+
errors.push(`Password must be at least ${config.minLength} characters long`);
|
|
71
|
+
hints.push(`Use at least ${config.minLength} characters`);
|
|
72
|
+
} else {
|
|
73
|
+
score += 20;
|
|
74
|
+
if (password.length < 12) {
|
|
75
|
+
hints.push("Use 12 or more characters for extra security");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (config.requireUppercase && !/[A-Z]/.test(password)) {
|
|
79
|
+
errors.push("Password must contain at least one uppercase letter");
|
|
80
|
+
hints.push("Add an uppercase letter (A-Z)");
|
|
81
|
+
} else if (/[A-Z]/.test(password)) {
|
|
82
|
+
score += 15;
|
|
83
|
+
}
|
|
84
|
+
if (config.requireLowercase && !/[a-z]/.test(password)) {
|
|
85
|
+
errors.push("Password must contain at least one lowercase letter");
|
|
86
|
+
hints.push("Add a lowercase letter (a-z)");
|
|
87
|
+
} else if (/[a-z]/.test(password)) {
|
|
88
|
+
score += 15;
|
|
89
|
+
}
|
|
90
|
+
if (config.requireNumbers && !/\d/.test(password)) {
|
|
91
|
+
errors.push("Password must contain at least one number");
|
|
92
|
+
hints.push("Add a number (0-9)");
|
|
93
|
+
} else if (/\d/.test(password)) {
|
|
94
|
+
score += 15;
|
|
95
|
+
}
|
|
96
|
+
if (config.requireSpecialChars && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
97
|
+
errors.push("Password must contain at least one special character");
|
|
98
|
+
hints.push("Add a special character (e.g. !@#$%)");
|
|
99
|
+
} else if (/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
100
|
+
score += 15;
|
|
101
|
+
}
|
|
102
|
+
if (config.preventCommonPasswords && COMMON_PASSWORDS.includes(password.toLowerCase())) {
|
|
103
|
+
errors.push("Password is too common. Please choose a more unique password");
|
|
104
|
+
hints.push("Avoid common passwords or names");
|
|
105
|
+
}
|
|
106
|
+
if (password.length >= 12) {
|
|
107
|
+
score += 10;
|
|
108
|
+
} else if (password.length >= 10) {
|
|
109
|
+
score += 5;
|
|
110
|
+
}
|
|
111
|
+
const hasUppercase = /[A-Z]/.test(password);
|
|
112
|
+
const hasLowercase = /[a-z]/.test(password);
|
|
113
|
+
const hasNumbers = /\d/.test(password);
|
|
114
|
+
const hasSpecial = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password);
|
|
115
|
+
const complexityTypes = [hasUppercase, hasLowercase, hasNumbers, hasSpecial].filter(Boolean).length;
|
|
116
|
+
if (complexityTypes >= 4) {
|
|
117
|
+
score += 10;
|
|
118
|
+
} else if (complexityTypes >= 3) {
|
|
119
|
+
score += 5;
|
|
120
|
+
hints.push("Use a mix of uppercase, lowercase, numbers, and special characters");
|
|
121
|
+
}
|
|
122
|
+
let strength = "weak";
|
|
123
|
+
if (score >= 80) {
|
|
124
|
+
strength = "strong";
|
|
125
|
+
} else if (score >= 60) {
|
|
126
|
+
strength = "medium";
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
isValid: errors.length === 0,
|
|
130
|
+
errors,
|
|
131
|
+
strength,
|
|
132
|
+
score: Math.min(score, 100),
|
|
133
|
+
hints: Array.from(new Set(hints))
|
|
134
|
+
// deduplicate
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
const getPasswordStrengthColor = (strength) => {
|
|
138
|
+
switch (strength) {
|
|
139
|
+
case "weak":
|
|
140
|
+
return "#dc3545";
|
|
141
|
+
case "medium":
|
|
142
|
+
return "#ffc107";
|
|
143
|
+
case "strong":
|
|
144
|
+
return "#28a745";
|
|
145
|
+
default:
|
|
146
|
+
return "#6c757d";
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const getPasswordStrengthText = (strength) => {
|
|
150
|
+
switch (strength) {
|
|
151
|
+
case "weak":
|
|
152
|
+
return "Weak";
|
|
153
|
+
case "medium":
|
|
154
|
+
return "Medium";
|
|
155
|
+
case "strong":
|
|
156
|
+
return "Strong";
|
|
157
|
+
default:
|
|
158
|
+
return "Unknown";
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export { getPasswordStrengthColor, getPasswordStrengthText, getPasswordValidationOptions, validatePassword };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-users",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "A comprehensive user management module for Nuxt 3 and Nuxt 4 applications with authentication, authorization, database support, and CLI tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nuxt",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"@nuxt/devtools": "^2.6.2",
|
|
112
112
|
"@nuxt/eslint-config": "^1.5.2",
|
|
113
113
|
"@nuxt/kit": "^3.17.6",
|
|
114
|
-
"@nuxt/module-builder": "^1.0.
|
|
114
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
115
115
|
"@nuxt/schema": "^3.17.6",
|
|
116
116
|
"@nuxt/test-utils": "^3.19.2",
|
|
117
117
|
"@types/bcrypt": "^6.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{--color-primary:#60a5fa;--color-primary-dark:#3b82f6;--color-primary-light:#93c5fd;--color-secondary:#10b981;--color-secondary-dark:#059669;--color-secondary-light:#34d399;--color-white:#000;--color-black:#fff;--color-gray-50:#111827;--color-gray-100:#1f2937;--color-gray-200:#374151;--color-gray-300:#4b5563;--color-gray-400:#6b7280;--color-gray-500:#9ca3af;--color-gray-600:#d1d5db;--color-gray-700:#e5e7eb;--color-gray-800:#f3f4f6;--color-gray-900:#f9fafb;--color-success:#10b981;--color-success-light:#064e3b;--color-warning:#fbbf24;--color-warning-light:#451a03;--color-warning-text:#fef3c7;--color-error:#ef4444;--color-error-light:#450a0a;--color-error-border:#7f1d1d;--color-error-alt:#f87171;--color-border:#374151;--color-border-light:#4b5563;--color-border-dark:#6b7280;--color-bg-primary:#111827;--color-bg-secondary:#1f2937;--color-bg-tertiary:#374151}@media (prefers-color-scheme:light){.light,:root{--color-primary:#3b82f6;--color-primary-dark:#2563eb;--color-primary-light:#60a5fa;--color-secondary:#059669;--color-secondary-dark:#047857;--color-secondary-light:#10b981;--color-white:#fff;--color-black:#000;--color-gray-50:#f9fafb;--color-gray-100:#f3f4f6;--color-gray-200:#e5e7eb;--color-gray-300:#d1d5db;--color-gray-400:#9ca3af;--color-gray-500:#6b7280;--color-gray-600:#4b5563;--color-gray-700:#374151;--color-gray-800:#1f2937;--color-gray-900:#111827;--color-success:#059669;--color-success-light:#f0fdf4;--color-warning:#f59e0b;--color-warning-light:#fef3c7;--color-warning-text:#92400e;--color-error:#dc2626;--color-error-light:#fef2f2;--color-error-border:#fecaca;--color-error-alt:#db5050;--color-border:#e5e7eb;--color-border-light:#d1d5db;--color-border-dark:#ccc;--color-bg-primary:#fff;--color-bg-secondary:#f9fafb;--color-bg-tertiary:#f3f4f6}}
|