nuxt-users 1.38.0 → 1.40.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/README.md +13 -1
- package/dist/cli.mjs +57 -27
- package/dist/module.d.mts +2 -2
- package/dist/module.d.ts +2 -2
- package/dist/module.json +4 -1
- package/dist/module.mjs +29 -7
- package/dist/runtime/assets/nuxt-users.css +1 -1
- package/dist/runtime/components/NUsersEmailConfirmation.d.vue.ts +5 -3
- package/dist/runtime/components/NUsersEmailConfirmation.vue +22 -15
- package/dist/runtime/components/NUsersEmailConfirmation.vue.d.ts +5 -3
- package/dist/runtime/components/NUsersGoogleLoginButton.d.vue.ts +4 -2
- package/dist/runtime/components/NUsersGoogleLoginButton.vue +5 -2
- package/dist/runtime/components/NUsersGoogleLoginButton.vue.d.ts +4 -2
- package/dist/runtime/components/NUsersList.vue +25 -11
- package/dist/runtime/components/NUsersLoginForm.d.vue.ts +13 -2
- package/dist/runtime/components/NUsersLoginForm.vue +35 -13
- package/dist/runtime/components/NUsersLoginForm.vue.d.ts +13 -2
- package/dist/runtime/components/NUsersLogoutLink.d.vue.ts +3 -4
- package/dist/runtime/components/NUsersLogoutLink.vue +8 -5
- package/dist/runtime/components/NUsersLogoutLink.vue.d.ts +3 -4
- package/dist/runtime/components/NUsersPasswordStrengthIndicator.d.vue.ts +6 -0
- package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue +17 -9
- package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue.d.ts +6 -0
- package/dist/runtime/components/NUsersProfileInfo.d.vue.ts +13 -1
- package/dist/runtime/components/NUsersProfileInfo.vue +23 -9
- package/dist/runtime/components/NUsersProfileInfo.vue.d.ts +13 -1
- package/dist/runtime/components/NUsersRegisterForm.d.vue.ts +26 -11
- package/dist/runtime/components/NUsersRegisterForm.vue +34 -16
- package/dist/runtime/components/NUsersRegisterForm.vue.d.ts +26 -11
- package/dist/runtime/components/NUsersResetPasswordForm.d.vue.ts +11 -2
- package/dist/runtime/components/NUsersResetPasswordForm.vue +25 -12
- package/dist/runtime/components/NUsersResetPasswordForm.vue.d.ts +11 -2
- package/dist/runtime/components/NUsersUserCard.d.vue.ts +2 -0
- package/dist/runtime/components/NUsersUserCard.vue +8 -4
- package/dist/runtime/components/NUsersUserCard.vue.d.ts +2 -0
- package/dist/runtime/components/NUsersUserForm.d.vue.ts +2 -2
- package/dist/runtime/components/NUsersUserForm.vue +5 -4
- package/dist/runtime/components/NUsersUserForm.vue.d.ts +2 -2
- package/dist/runtime/composables/useAuthentication.js +14 -0
- package/dist/runtime/composables/useNuxtUsersLocale.d.ts +15 -0
- package/dist/runtime/composables/useNuxtUsersLocale.js +27 -0
- package/dist/runtime/composables/usePasswordValidation.d.ts +1 -1
- package/dist/runtime/composables/usePublicPaths.js +2 -1
- package/dist/runtime/locales/en.d.ts +2 -0
- package/dist/runtime/locales/en.js +130 -0
- package/dist/runtime/locales/hu.d.ts +3 -0
- package/dist/runtime/locales/hu.js +188 -0
- package/dist/runtime/locales/index.d.ts +5 -0
- package/dist/runtime/locales/index.js +8 -0
- package/dist/runtime/server/api/nuxt-users/[id].delete.js +1 -1
- package/dist/runtime/server/api/nuxt-users/[id].get.js +1 -1
- package/dist/runtime/server/api/nuxt-users/[id].patch.js +1 -1
- package/dist/runtime/server/api/nuxt-users/auth/google/callback.get.js +1 -1
- package/dist/runtime/server/api/nuxt-users/auth/google/redirect.get.js +1 -1
- package/dist/runtime/server/api/nuxt-users/confirm-email.get.js +3 -3
- package/dist/runtime/server/api/nuxt-users/inactive.get.js +1 -2
- package/dist/runtime/server/api/nuxt-users/index.get.js +1 -2
- package/dist/runtime/server/api/nuxt-users/index.post.js +1 -1
- package/dist/runtime/server/api/nuxt-users/me.get.js +20 -13
- package/dist/runtime/server/api/nuxt-users/me.patch.js +1 -1
- package/dist/runtime/server/api/nuxt-users/password/forgot.post.js +2 -1
- package/dist/runtime/server/api/nuxt-users/password/index.patch.js +1 -1
- package/dist/runtime/server/api/nuxt-users/password/reset.post.d.ts +1 -1
- package/dist/runtime/server/api/nuxt-users/password/reset.post.js +4 -2
- package/dist/runtime/server/api/nuxt-users/session/index.delete.js +1 -1
- package/dist/runtime/server/api/nuxt-users/session/index.post.js +5 -3
- package/dist/runtime/server/composables/useServerAuth.js +1 -1
- package/dist/runtime/server/internal/index.d.ts +1 -7
- package/dist/runtime/server/internal/index.js +23 -7
- package/dist/runtime/server/middleware/authorization.server.js +22 -5
- package/dist/runtime/server/tasks/cleanup-tokens.d.ts +1 -1
- package/dist/runtime/server/utils/add-indexes.d.ts +3 -0
- package/dist/runtime/server/utils/add-indexes.js +17 -0
- package/dist/runtime/server/utils/google-oauth.js +2 -1
- package/dist/runtime/server/utils/index.d.ts +2 -2
- package/dist/runtime/server/utils/index.js +2 -2
- package/dist/runtime/server/utils/migrate.d.ts +6 -0
- package/dist/runtime/server/utils/migrate.js +10 -1
- package/dist/runtime/utils/deep-merge.d.ts +8 -0
- package/dist/runtime/utils/deep-merge.js +17 -0
- package/dist/runtime/utils/locale.d.ts +14 -0
- package/dist/runtime/utils/locale.js +38 -0
- package/dist/shared/{nuxt-users.BnOQs2LI.mjs → nuxt-users.DfZ7bc_P.mjs} +16 -8
- package/dist/shared/{nuxt-users.SXaMnaEL.d.mts → nuxt-users.XW6DYM7t.d.mts} +46 -2
- package/dist/shared/{nuxt-users.SXaMnaEL.d.ts → nuxt-users.XW6DYM7t.d.ts} +46 -2
- package/dist/utils.d.mts +4 -1
- package/dist/utils.d.ts +4 -1
- package/dist/utils.mjs +4 -1
- package/package.json +11 -6
- package/scripts/check-locale-consistency.ts +156 -0
- package/scripts/check-peer-dependencies.js +5 -5
- package/scripts/check-translations.ts +255 -0
- package/scripts/post-build.js +1 -1
- package/scripts/test-nuxt3.sh +78 -0
|
@@ -28,11 +28,17 @@ export default defineEventHandler(async (event) => {
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
const token = getCookie(event, "auth_token");
|
|
31
|
+
const isMeEndpoint = event.path === `${base}/me`;
|
|
31
32
|
if (!token) {
|
|
32
33
|
if (event.path.startsWith("/api/")) {
|
|
33
|
-
|
|
34
|
+
if (isMeEndpoint) {
|
|
35
|
+
console.debug(`[Nuxt Users] authorization: ${event.path} No token found - expected for auth check`);
|
|
36
|
+
} else {
|
|
37
|
+
console.warn(`[Nuxt Users] authorization: ${event.path} No token found - API request rejected`);
|
|
38
|
+
}
|
|
34
39
|
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
35
|
-
}
|
|
40
|
+
}
|
|
41
|
+
if (!event.path.startsWith("/api/")) {
|
|
36
42
|
console.debug(`[Nuxt Users] authorization: ${event.path} No token found - letting client handle page redirect`);
|
|
37
43
|
return;
|
|
38
44
|
}
|
|
@@ -40,13 +46,23 @@ export default defineEventHandler(async (event) => {
|
|
|
40
46
|
const user = await getCurrentUserFromToken(token, options);
|
|
41
47
|
if (!user) {
|
|
42
48
|
if (event.path.startsWith("/api/")) {
|
|
43
|
-
|
|
49
|
+
if (isMeEndpoint) {
|
|
50
|
+
console.debug(`[Nuxt Users] authorization: ${event.path} Invalid token - expected for auth check`);
|
|
51
|
+
} else {
|
|
52
|
+
console.warn(`[Nuxt Users] authorization: ${event.path} Invalid token - API request rejected`);
|
|
53
|
+
}
|
|
44
54
|
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
45
|
-
}
|
|
55
|
+
}
|
|
56
|
+
if (!event.path.startsWith("/api/")) {
|
|
46
57
|
console.debug(`[Nuxt Users] authorization: ${event.path} Invalid token - letting client handle page redirect`);
|
|
47
58
|
return;
|
|
48
59
|
}
|
|
49
60
|
}
|
|
61
|
+
if (!event.context) {
|
|
62
|
+
event.context = {};
|
|
63
|
+
}
|
|
64
|
+
event.context.nuxtUsers = event.context.nuxtUsers || {};
|
|
65
|
+
event.context.nuxtUsers.user = user;
|
|
50
66
|
if (event.path === `${base}/me`) {
|
|
51
67
|
console.debug(`[Nuxt Users] authorization: Auto-whitelisted /me endpoint for authenticated user ${user.id}`);
|
|
52
68
|
return;
|
|
@@ -55,7 +71,8 @@ export default defineEventHandler(async (event) => {
|
|
|
55
71
|
if (event.path.startsWith("/api/")) {
|
|
56
72
|
console.warn(`[Nuxt Users] authorization: ${event.path} User ${user.id} with role ${user.role} denied access - API request rejected`);
|
|
57
73
|
throw createError({ statusCode: 403, statusMessage: "Forbidden" });
|
|
58
|
-
}
|
|
74
|
+
}
|
|
75
|
+
if (!event.path.startsWith("/api/")) {
|
|
59
76
|
console.debug(`[Nuxt Users] authorization: ${event.path} User ${user.id} with role ${user.role} denied access - letting client handle page redirect`);
|
|
60
77
|
return;
|
|
61
78
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("nitropack").Task<
|
|
1
|
+
declare const _default: import("nitropack").Task<string>;
|
|
2
2
|
export default _default;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useDb } from "./db.js";
|
|
2
|
+
export const addActiveIndexToUsers = async (options) => {
|
|
3
|
+
const connectorName = options.connector.name;
|
|
4
|
+
const db = await useDb(options);
|
|
5
|
+
const tableName = options.tables.users;
|
|
6
|
+
console.log(`[Nuxt Users] DB:Add active index to ${connectorName} Users Table in ${tableName}...`);
|
|
7
|
+
await db.sql`CREATE INDEX IF NOT EXISTS idx_users_active ON {${tableName}} (active)`;
|
|
8
|
+
console.log(`[Nuxt Users] DB:Add active index to ${connectorName} Users Table \u2705`);
|
|
9
|
+
};
|
|
10
|
+
export const addTokenableIndexToPersonalAccessTokens = async (options) => {
|
|
11
|
+
const connectorName = options.connector.name;
|
|
12
|
+
const db = await useDb(options);
|
|
13
|
+
const tableName = options.tables.personalAccessTokens;
|
|
14
|
+
console.log(`[Nuxt Users] DB:Add tokenable index to ${connectorName} Personal Access Tokens Table in ${tableName}...`);
|
|
15
|
+
await db.sql`CREATE INDEX IF NOT EXISTS idx_personal_access_tokens_tokenable ON {${tableName}} (tokenable_type, tokenable_id)`;
|
|
16
|
+
console.log(`[Nuxt Users] DB:Add tokenable index to ${connectorName} Personal Access Tokens Table \u2705`);
|
|
17
|
+
};
|
|
@@ -122,7 +122,8 @@ export async function createAuthTokenForUser(user, moduleOptions, rememberMe = f
|
|
|
122
122
|
if (rememberMe) {
|
|
123
123
|
const longTermDays = moduleOptions.auth.rememberMeExpiration || 30;
|
|
124
124
|
expiresAt.setDate(expiresAt.getDate() + longTermDays);
|
|
125
|
-
}
|
|
125
|
+
}
|
|
126
|
+
if (!rememberMe) {
|
|
126
127
|
expiresAt.setMinutes(expiresAt.getMinutes() + moduleOptions.auth.tokenExpiration);
|
|
127
128
|
}
|
|
128
129
|
await db.sql`
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { useDb, checkTableExists } from './db.js';
|
|
1
|
+
export { useDb, checkTableExists, getConnector, closeAllDbConnections } from './db.js';
|
|
2
2
|
export { isBuildTime } from './build-time.js';
|
|
3
3
|
export { createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens, getLastLoginTime, findUserById, updateUser, deleteUser } from './user.js';
|
|
4
4
|
export { createUsersTable } from './create-users-table.js';
|
|
5
5
|
export { createPersonalAccessTokensTable } from './create-personal-access-tokens-table.js';
|
|
6
6
|
export { createPasswordResetTokensTable } from './create-password-reset-tokens-table.js';
|
|
7
7
|
export { createMigrationsTable } from './create-migrations-table.js';
|
|
8
|
-
export { runMigrations } from './migrate.js';
|
|
8
|
+
export { runMigrations, getAppliedMigrations } from './migrate.js';
|
|
9
9
|
export { createGoogleOAuth2Client, getGoogleAuthUrl, getGoogleUserFromCode, findOrCreateGoogleUser, createAuthTokenForUser, generateSecurePassword } from './google-oauth.js';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { useDb, checkTableExists } from "./db.js";
|
|
1
|
+
export { useDb, checkTableExists, getConnector, closeAllDbConnections } from "./db.js";
|
|
2
2
|
export { isBuildTime } from "./build-time.js";
|
|
3
3
|
export { createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens, getLastLoginTime, findUserById, updateUser, deleteUser } from "./user.js";
|
|
4
4
|
export { createUsersTable } from "./create-users-table.js";
|
|
5
5
|
export { createPersonalAccessTokensTable } from "./create-personal-access-tokens-table.js";
|
|
6
6
|
export { createPasswordResetTokensTable } from "./create-password-reset-tokens-table.js";
|
|
7
7
|
export { createMigrationsTable } from "./create-migrations-table.js";
|
|
8
|
-
export { runMigrations } from "./migrate.js";
|
|
8
|
+
export { runMigrations, getAppliedMigrations } from "./migrate.js";
|
|
9
9
|
export { createGoogleOAuth2Client, getGoogleAuthUrl, getGoogleUserFromCode, findOrCreateGoogleUser, createAuthTokenForUser, generateSecurePassword } from "./google-oauth.js";
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { ModuleOptions } from 'nuxt-users/utils';
|
|
2
|
+
interface Migration {
|
|
3
|
+
name: string;
|
|
4
|
+
run: (options: ModuleOptions) => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare const migrations: Migration[];
|
|
2
7
|
export declare const getAppliedMigrations: (options: ModuleOptions) => Promise<string[]>;
|
|
3
8
|
export declare const markMigrationAsApplied: (options: ModuleOptions, migrationName: string) => Promise<void>;
|
|
4
9
|
export declare const runMigrations: (options: ModuleOptions) => Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -5,7 +5,8 @@ import { createPasswordResetTokensTable } from "./create-password-reset-tokens-t
|
|
|
5
5
|
import { createMigrationsTable } from "./create-migrations-table.js";
|
|
6
6
|
import { addActiveToUsers } from "./add-active-to-users.js";
|
|
7
7
|
import { addGoogleOauthFields } from "./add-google-oauth-fields.js";
|
|
8
|
-
|
|
8
|
+
import { addActiveIndexToUsers, addTokenableIndexToPersonalAccessTokens } from "./add-indexes.js";
|
|
9
|
+
export const migrations = [
|
|
9
10
|
{
|
|
10
11
|
name: "create_migrations_table",
|
|
11
12
|
run: createMigrationsTable
|
|
@@ -29,6 +30,14 @@ const migrations = [
|
|
|
29
30
|
{
|
|
30
31
|
name: "add_google_oauth_fields",
|
|
31
32
|
run: addGoogleOauthFields
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "add_active_index_to_users",
|
|
36
|
+
run: addActiveIndexToUsers
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "add_tokenable_index_to_personal_access_tokens",
|
|
40
|
+
run: addTokenableIndexToPersonalAccessTokens
|
|
32
41
|
}
|
|
33
42
|
];
|
|
34
43
|
export const getAppliedMigrations = async (options) => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LocaleMessages } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deep merge two objects
|
|
4
|
+
* @param target - Base object
|
|
5
|
+
* @param source - Object to merge into target
|
|
6
|
+
* @returns Merged object
|
|
7
|
+
*/
|
|
8
|
+
export declare const deepMerge: (target: LocaleMessages, source: LocaleMessages) => LocaleMessages;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const deepMerge = (target, source) => {
|
|
2
|
+
const result = { ...target };
|
|
3
|
+
for (const key in source) {
|
|
4
|
+
const sourceValue = source[key];
|
|
5
|
+
if (!sourceValue) {
|
|
6
|
+
result[key] = sourceValue;
|
|
7
|
+
} else if (typeof sourceValue === "object" && !Array.isArray(sourceValue)) {
|
|
8
|
+
result[key] = deepMerge(
|
|
9
|
+
result[key] || {},
|
|
10
|
+
sourceValue
|
|
11
|
+
);
|
|
12
|
+
} else {
|
|
13
|
+
result[key] = sourceValue;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LocaleMessages } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Get a nested value from an object using dot notation
|
|
4
|
+
*/
|
|
5
|
+
export declare const getNestedValue: (obj: LocaleMessages, path: string) => string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Get translation for a key
|
|
8
|
+
* @param key - Translation key (e.g., 'login.title')
|
|
9
|
+
* @param locale - Current locale
|
|
10
|
+
* @param customTexts - Custom texts from config
|
|
11
|
+
* @param fallbackLocale - Fallback locale
|
|
12
|
+
* @param params - Parameters to replace in the translation (e.g., {0}, {1})
|
|
13
|
+
*/
|
|
14
|
+
export declare const getTranslation: (key: string, locale?: string, customTexts?: Record<string, LocaleMessages>, fallbackLocale?: string, params?: (string | number)[]) => string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defaultLocaleMessages } from "../locales/index.js";
|
|
2
|
+
import { deepMerge } from "./deep-merge.js";
|
|
3
|
+
export const getNestedValue = (obj, path) => {
|
|
4
|
+
const keys = path.split(".");
|
|
5
|
+
let value = obj;
|
|
6
|
+
for (const key of keys) {
|
|
7
|
+
if (value && typeof value === "object") {
|
|
8
|
+
value = value[key];
|
|
9
|
+
} else {
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return typeof value === "string" ? value : void 0;
|
|
14
|
+
};
|
|
15
|
+
export const getTranslation = (key, locale = "en", customTexts, fallbackLocale = "en", params) => {
|
|
16
|
+
const localeMessages = customTexts?.[locale] ? deepMerge(defaultLocaleMessages[locale] || {}, customTexts[locale]) : defaultLocaleMessages[locale];
|
|
17
|
+
let translation = localeMessages ? getNestedValue(localeMessages, key) : void 0;
|
|
18
|
+
if (!translation && fallbackLocale !== locale) {
|
|
19
|
+
const fallbackMessages = customTexts?.[fallbackLocale] ? deepMerge(defaultLocaleMessages[fallbackLocale] || {}, customTexts[fallbackLocale]) : defaultLocaleMessages[fallbackLocale];
|
|
20
|
+
translation = fallbackMessages ? getNestedValue(fallbackMessages, key) : void 0;
|
|
21
|
+
}
|
|
22
|
+
if (!translation && locale !== "en" && fallbackLocale !== "en" && defaultLocaleMessages.en) {
|
|
23
|
+
translation = getNestedValue(defaultLocaleMessages.en, key);
|
|
24
|
+
}
|
|
25
|
+
if (translation && params && params.length > 0) {
|
|
26
|
+
const placeholderMatches = translation.match(/\{\d+\}/g);
|
|
27
|
+
const expectedParamCount = placeholderMatches ? placeholderMatches.length : 0;
|
|
28
|
+
if (expectedParamCount !== params.length && process.env.NODE_ENV !== "production") {
|
|
29
|
+
console.warn(
|
|
30
|
+
`[nuxt-users locale] Parameter count mismatch for key "${key}": expected ${expectedParamCount} parameter(s), but received ${params.length}`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
params.forEach((param, index) => {
|
|
34
|
+
translation = translation.replace(`{${index}}`, String(param));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return translation || key;
|
|
38
|
+
};
|
|
@@ -69,7 +69,8 @@ const validatePassword = (password, options = {}) => {
|
|
|
69
69
|
if (password.length < config.minLength) {
|
|
70
70
|
errors.push(`Password must be at least ${config.minLength} characters long`);
|
|
71
71
|
hints.push(`Use at least ${config.minLength} characters`);
|
|
72
|
-
}
|
|
72
|
+
}
|
|
73
|
+
if (password.length >= config.minLength) {
|
|
73
74
|
score += 20;
|
|
74
75
|
if (password.length < 12) {
|
|
75
76
|
hints.push("Use 12 or more characters for extra security");
|
|
@@ -78,25 +79,29 @@ const validatePassword = (password, options = {}) => {
|
|
|
78
79
|
if (config.requireUppercase && !/[A-Z]/.test(password)) {
|
|
79
80
|
errors.push("Password must contain at least one uppercase letter");
|
|
80
81
|
hints.push("Add an uppercase letter (A-Z)");
|
|
81
|
-
}
|
|
82
|
+
}
|
|
83
|
+
if (/[A-Z]/.test(password)) {
|
|
82
84
|
score += 15;
|
|
83
85
|
}
|
|
84
86
|
if (config.requireLowercase && !/[a-z]/.test(password)) {
|
|
85
87
|
errors.push("Password must contain at least one lowercase letter");
|
|
86
88
|
hints.push("Add a lowercase letter (a-z)");
|
|
87
|
-
}
|
|
89
|
+
}
|
|
90
|
+
if (/[a-z]/.test(password)) {
|
|
88
91
|
score += 15;
|
|
89
92
|
}
|
|
90
93
|
if (config.requireNumbers && !/\d/.test(password)) {
|
|
91
94
|
errors.push("Password must contain at least one number");
|
|
92
95
|
hints.push("Add a number (0-9)");
|
|
93
|
-
}
|
|
96
|
+
}
|
|
97
|
+
if (/\d/.test(password)) {
|
|
94
98
|
score += 15;
|
|
95
99
|
}
|
|
96
100
|
if (config.requireSpecialChars && !/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
97
101
|
errors.push("Password must contain at least one special character");
|
|
98
102
|
hints.push("Add a special character (e.g. !@#$%)");
|
|
99
|
-
}
|
|
103
|
+
}
|
|
104
|
+
if (/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(password)) {
|
|
100
105
|
score += 15;
|
|
101
106
|
}
|
|
102
107
|
if (config.preventCommonPasswords && COMMON_PASSWORDS.includes(password.toLowerCase())) {
|
|
@@ -105,7 +110,8 @@ const validatePassword = (password, options = {}) => {
|
|
|
105
110
|
}
|
|
106
111
|
if (password.length >= 12) {
|
|
107
112
|
score += 10;
|
|
108
|
-
}
|
|
113
|
+
}
|
|
114
|
+
if (password.length >= 10 && password.length < 12) {
|
|
109
115
|
score += 5;
|
|
110
116
|
}
|
|
111
117
|
const hasUppercase = /[A-Z]/.test(password);
|
|
@@ -115,14 +121,16 @@ const validatePassword = (password, options = {}) => {
|
|
|
115
121
|
const complexityTypes = [hasUppercase, hasLowercase, hasNumbers, hasSpecial].filter(Boolean).length;
|
|
116
122
|
if (complexityTypes >= 4) {
|
|
117
123
|
score += 10;
|
|
118
|
-
}
|
|
124
|
+
}
|
|
125
|
+
if (complexityTypes >= 3 && complexityTypes < 4) {
|
|
119
126
|
score += 5;
|
|
120
127
|
hints.push("Use a mix of uppercase, lowercase, numbers, and special characters");
|
|
121
128
|
}
|
|
122
129
|
let strength = "weak";
|
|
123
130
|
if (score >= 80) {
|
|
124
131
|
strength = "strong";
|
|
125
|
-
}
|
|
132
|
+
}
|
|
133
|
+
if (score >= 60 && score < 80) {
|
|
126
134
|
strength = "medium";
|
|
127
135
|
}
|
|
128
136
|
return {
|
|
@@ -12,6 +12,35 @@ type DatabaseConfig = {
|
|
|
12
12
|
password?: string;
|
|
13
13
|
database?: string;
|
|
14
14
|
};
|
|
15
|
+
interface LocaleMessages {
|
|
16
|
+
[key: string]: string | LocaleMessages;
|
|
17
|
+
}
|
|
18
|
+
interface LocaleOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Default locale code
|
|
21
|
+
* @default 'en'
|
|
22
|
+
*/
|
|
23
|
+
default?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Custom messages to override or extend default translations
|
|
26
|
+
* Use nested object structure for organizing translations
|
|
27
|
+
* @example
|
|
28
|
+
* {
|
|
29
|
+
* en: {
|
|
30
|
+
* login: { title: 'Welcome', submit: 'Sign In' }
|
|
31
|
+
* },
|
|
32
|
+
* hu: {
|
|
33
|
+
* login: { title: 'Üdvözöljük', submit: 'Bejelentkezés' }
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
*/
|
|
37
|
+
texts?: Record<string, LocaleMessages>;
|
|
38
|
+
/**
|
|
39
|
+
* Fallback locale when translation is missing
|
|
40
|
+
* @default 'en'
|
|
41
|
+
*/
|
|
42
|
+
fallbackLocale?: string;
|
|
43
|
+
}
|
|
15
44
|
interface GoogleOAuthOptions {
|
|
16
45
|
/**
|
|
17
46
|
* Google OAuth client ID from Google Cloud Console
|
|
@@ -159,6 +188,10 @@ interface RuntimeModuleOptions {
|
|
|
159
188
|
* @default false
|
|
160
189
|
*/
|
|
161
190
|
hardDelete?: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Locale configuration for component labels and messages
|
|
193
|
+
*/
|
|
194
|
+
locale?: LocaleOptions;
|
|
162
195
|
}
|
|
163
196
|
interface ModuleOptions {
|
|
164
197
|
connector: {
|
|
@@ -191,6 +224,17 @@ interface ModuleOptions {
|
|
|
191
224
|
preventCommonPasswords: boolean;
|
|
192
225
|
};
|
|
193
226
|
hardDelete: boolean;
|
|
227
|
+
locale?: {
|
|
228
|
+
default: string;
|
|
229
|
+
fallbackLocale?: string;
|
|
230
|
+
texts?: Record<string, LocaleMessages>;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Token cleanup schedule (cron expression)
|
|
234
|
+
* Set to null or false to disable automatic cleanup
|
|
235
|
+
* Default: '0 2 * * *' (daily at 2 AM)
|
|
236
|
+
*/
|
|
237
|
+
tokenCleanupSchedule?: string | false | null;
|
|
194
238
|
}
|
|
195
239
|
interface MailerOptions {
|
|
196
240
|
host: string;
|
|
@@ -268,5 +312,5 @@ declare const defaultFieldLabels: {
|
|
|
268
312
|
last_login_at: string;
|
|
269
313
|
};
|
|
270
314
|
|
|
271
|
-
export { defaultDisplayFields as
|
|
272
|
-
export type { DisplayFieldsProps as D, GoogleOAuthOptions as G, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g };
|
|
315
|
+
export { defaultDisplayFields as j, defaultFieldLabels as k };
|
|
316
|
+
export type { DisplayFieldsProps as D, GoogleOAuthOptions as G, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g, LocaleOptions as h, LocaleMessages as i };
|
|
@@ -12,6 +12,35 @@ type DatabaseConfig = {
|
|
|
12
12
|
password?: string;
|
|
13
13
|
database?: string;
|
|
14
14
|
};
|
|
15
|
+
interface LocaleMessages {
|
|
16
|
+
[key: string]: string | LocaleMessages;
|
|
17
|
+
}
|
|
18
|
+
interface LocaleOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Default locale code
|
|
21
|
+
* @default 'en'
|
|
22
|
+
*/
|
|
23
|
+
default?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Custom messages to override or extend default translations
|
|
26
|
+
* Use nested object structure for organizing translations
|
|
27
|
+
* @example
|
|
28
|
+
* {
|
|
29
|
+
* en: {
|
|
30
|
+
* login: { title: 'Welcome', submit: 'Sign In' }
|
|
31
|
+
* },
|
|
32
|
+
* hu: {
|
|
33
|
+
* login: { title: 'Üdvözöljük', submit: 'Bejelentkezés' }
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
*/
|
|
37
|
+
texts?: Record<string, LocaleMessages>;
|
|
38
|
+
/**
|
|
39
|
+
* Fallback locale when translation is missing
|
|
40
|
+
* @default 'en'
|
|
41
|
+
*/
|
|
42
|
+
fallbackLocale?: string;
|
|
43
|
+
}
|
|
15
44
|
interface GoogleOAuthOptions {
|
|
16
45
|
/**
|
|
17
46
|
* Google OAuth client ID from Google Cloud Console
|
|
@@ -159,6 +188,10 @@ interface RuntimeModuleOptions {
|
|
|
159
188
|
* @default false
|
|
160
189
|
*/
|
|
161
190
|
hardDelete?: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Locale configuration for component labels and messages
|
|
193
|
+
*/
|
|
194
|
+
locale?: LocaleOptions;
|
|
162
195
|
}
|
|
163
196
|
interface ModuleOptions {
|
|
164
197
|
connector: {
|
|
@@ -191,6 +224,17 @@ interface ModuleOptions {
|
|
|
191
224
|
preventCommonPasswords: boolean;
|
|
192
225
|
};
|
|
193
226
|
hardDelete: boolean;
|
|
227
|
+
locale?: {
|
|
228
|
+
default: string;
|
|
229
|
+
fallbackLocale?: string;
|
|
230
|
+
texts?: Record<string, LocaleMessages>;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Token cleanup schedule (cron expression)
|
|
234
|
+
* Set to null or false to disable automatic cleanup
|
|
235
|
+
* Default: '0 2 * * *' (daily at 2 AM)
|
|
236
|
+
*/
|
|
237
|
+
tokenCleanupSchedule?: string | false | null;
|
|
194
238
|
}
|
|
195
239
|
interface MailerOptions {
|
|
196
240
|
host: string;
|
|
@@ -268,5 +312,5 @@ declare const defaultFieldLabels: {
|
|
|
268
312
|
last_login_at: string;
|
|
269
313
|
};
|
|
270
314
|
|
|
271
|
-
export { defaultDisplayFields as
|
|
272
|
-
export type { DisplayFieldsProps as D, GoogleOAuthOptions as G, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g };
|
|
315
|
+
export { defaultDisplayFields as j, defaultFieldLabels as k };
|
|
316
|
+
export type { DisplayFieldsProps as D, GoogleOAuthOptions as G, LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b, LoginFormProps as c, ResetPasswordFormProps as d, Permission as e, DatabaseType as f, DatabaseConfig as g, LocaleOptions as h, LocaleMessages as i };
|
package/dist/utils.d.mts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
2
|
+
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
3
|
+
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
4
|
+
export { g as DatabaseConfig, f as DatabaseType, D as DisplayFieldsProps, G as GoogleOAuthOptions, i as LocaleMessages, h as LocaleOptions, L as LoginFormData, c as LoginFormProps, M as ModuleOptions, b as PasswordResetToken, e as Permission, P as PersonalAccessToken, d as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, a as UserWithoutPassword, j as defaultDisplayFields, k as defaultFieldLabels } from './shared/nuxt-users.XW6DYM7t.mjs';
|
|
2
5
|
|
|
3
6
|
interface PasswordValidationResult {
|
|
4
7
|
isValid: boolean;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
2
|
+
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
3
|
+
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
4
|
+
export { g as DatabaseConfig, f as DatabaseType, D as DisplayFieldsProps, G as GoogleOAuthOptions, i as LocaleMessages, h as LocaleOptions, L as LoginFormData, c as LoginFormProps, M as ModuleOptions, b as PasswordResetToken, e as Permission, P as PersonalAccessToken, d as ResetPasswordFormProps, R as RuntimeModuleOptions, U as User, a as UserWithoutPassword, j as defaultDisplayFields, k as defaultFieldLabels } from './shared/nuxt-users.XW6DYM7t.js';
|
|
2
5
|
|
|
3
6
|
interface PasswordValidationResult {
|
|
4
7
|
isValid: boolean;
|
package/dist/utils.mjs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export { a as getPasswordStrengthColor, b as getPasswordStrengthText, g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.
|
|
1
|
+
export { a as getPasswordStrengthColor, b as getPasswordStrengthText, g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.DfZ7bc_P.mjs';
|
|
2
|
+
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
3
|
+
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
4
|
+
export { deepMerge } from '../dist/runtime/utils/deep-merge.js';
|
|
2
5
|
|
|
3
6
|
const defaultDisplayFields = ["id", "name", "email", "role", "active", "created_at", "updated_at", "last_login_at"];
|
|
4
7
|
const defaultFieldLabels = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-users",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.40.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",
|
|
@@ -62,27 +62,32 @@
|
|
|
62
62
|
"dev:prepare": "yarn nuxt-module-build build --stub && yarn nuxt-module-build prepare && nuxi prepare playground",
|
|
63
63
|
"build": "nuxt-module-build build",
|
|
64
64
|
"postbuild": "node scripts/post-build.js",
|
|
65
|
+
"check:translations": "tsx scripts/check-translations.ts",
|
|
66
|
+
"check:locale-consistency": "tsx scripts/check-locale-consistency.ts",
|
|
67
|
+
"check:auth": "npm whoami --registry=https://registry.npmjs.org/ || (echo '❌ Not authenticated with npm. Please run: npm login' && exit 1)",
|
|
65
68
|
"prerelease": "node -v | grep -q 'v22' || (echo 'Please use Node.js v22 for deployment' && exit 1)",
|
|
66
|
-
"release": "yarn lint && yarn test && yarn prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
69
|
+
"release": "yarn check:auth && yarn lint && yarn check:translations && yarn check:locale-consistency && yarn test && yarn prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
67
70
|
"lint": "eslint .",
|
|
68
71
|
"lint:fix": "eslint . --fix",
|
|
69
72
|
"pretest": "node -v | grep -q 'v22' || (echo 'Please use Node.js v22 for testing' && exit 1)",
|
|
70
73
|
"test:quick": "yarn test:unit && yarn test:sqlite",
|
|
71
74
|
"test:release": "yarn test && yarn build && yarn lint:fix",
|
|
72
|
-
"test:types": "yarn dev:prepare && vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
|
|
75
|
+
"test:types": "yarn dev:prepare && vue-tsc --noEmit --project tsconfig.vitest.json && cd playground && vue-tsc --noEmit",
|
|
73
76
|
"test:unit": "./scripts/test-unit.sh",
|
|
74
77
|
"test:integration": "./scripts/test-integration.sh",
|
|
75
78
|
"test:sqlite": "./scripts/test-sqlite.sh",
|
|
76
79
|
"test:mysql": "./scripts/test-mysql.sh",
|
|
77
80
|
"test:postgresql": "./scripts/test-postgresql.sh",
|
|
78
|
-
"test": "
|
|
81
|
+
"test:nuxt3": "./scripts/test-nuxt3.sh unit && ./scripts/test-nuxt3.sh integration && ./scripts/test-nuxt3.sh sqlite && ./scripts/test-nuxt3.sh mysql && ./scripts/test-nuxt3.sh postgresql",
|
|
82
|
+
"test:nuxt4": "yarn test:types && yarn test:unit && yarn test:integration && yarn test:sqlite && yarn test:mysql && yarn test:postgresql",
|
|
83
|
+
"test": "yarn lint && yarn test:nuxt3 && yarn test:nuxt4",
|
|
79
84
|
"test:watch": "vitest watch",
|
|
80
85
|
"db:create-users-table": "tsx src/cli/create-users-table.ts",
|
|
81
86
|
"db:create-user": "tsx src/cli/create-user.ts",
|
|
82
87
|
"db:create-personal-access-tokens-table": "tsx src/cli/create-personal-access-tokens-table.ts",
|
|
83
88
|
"db:create-password-reset-tokens-table": "tsx src/cli/create-password-reset-tokens-table.ts",
|
|
84
89
|
"db:create-migrations-table": "tsx src/cli/create-migrations-table.ts",
|
|
85
|
-
"db:migrate": "tsx src/cli/
|
|
90
|
+
"db:migrate": "tsx src/cli/main.ts migrate",
|
|
86
91
|
"db:create-empty-sqlite": "node scripts/create-empty-sqlite.js",
|
|
87
92
|
"docs:dev": "vitepress dev docs",
|
|
88
93
|
"docs:build": "vitepress build docs",
|
|
@@ -121,7 +126,7 @@
|
|
|
121
126
|
"@nuxt/test-utils": "^3.19.2",
|
|
122
127
|
"@nuxt/vite-builder": "^4.2.0",
|
|
123
128
|
"@types/bcrypt": "^6.0.0",
|
|
124
|
-
"@types/node": "
|
|
129
|
+
"@types/node": "^25.0.3",
|
|
125
130
|
"@types/nodemailer": "^6.4.17",
|
|
126
131
|
"@types/pg": "^8.15.4",
|
|
127
132
|
"changelogen": "^0.6.2",
|