nuxt-users 1.8.4 → 1.9.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 CHANGED
@@ -1,6 +1,53 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## v2.0.0
5
+
6
+ [compare changes](https://github.com/rrd108/nuxt-users/compare/v1.8.5...v2.0.0)
7
+
8
+ ### 🚀 Enhancements
9
+
10
+ - ⚠️ Export TypeScript types and utilities for consumers ([729d961](https://github.com/rrd108/nuxt-users/commit/729d961))
11
+ - Add getLastLoginTime function to track user login activity ([001d4f2](https://github.com/rrd108/nuxt-users/commit/001d4f2))
12
+
13
+ ### 🏡 Chore
14
+
15
+ - Fix lint ([beb3e43](https://github.com/rrd108/nuxt-users/commit/beb3e43))
16
+ - **release:** V1.8.5 ([403cbd3](https://github.com/rrd108/nuxt-users/commit/403cbd3))
17
+
18
+ ### 🎨 Styles
19
+
20
+ - Fix code formatting and remove trailing whitespace ([ea07a3c](https://github.com/rrd108/nuxt-users/commit/ea07a3c))
21
+
22
+ #### ⚠️ Breaking Changes
23
+
24
+ - ⚠️ Export TypeScript types and utilities for consumers ([729d961](https://github.com/rrd108/nuxt-users/commit/729d961))
25
+
26
+ ### ❤️ Contributors
27
+
28
+ - Rrd108 ([@rrd108](https://github.com/rrd108))
29
+
30
+ ## v1.8.5
31
+
32
+ [compare changes](https://github.com/rrd108/nuxt-users/compare/v1.8.4...v1.8.5)
33
+
34
+ ### 🩹 Fixes
35
+
36
+ - Database sharing feature ([3a2e12f](https://github.com/rrd108/nuxt-users/commit/3a2e12f))
37
+
38
+ ### 🏡 Chore
39
+
40
+ - **release:** V1.8.4 ([ab7fa92](https://github.com/rrd108/nuxt-users/commit/ab7fa92))
41
+ - Fix lint ([beb3e43](https://github.com/rrd108/nuxt-users/commit/beb3e43))
42
+
43
+ ### ✅ Tests
44
+
45
+ - Add to check cli error ([fd338ca](https://github.com/rrd108/nuxt-users/commit/fd338ca))
46
+
47
+ ### ❤️ Contributors
48
+
49
+ - Rrd108 ([@rrd108](https://github.com/rrd108))
50
+
4
51
  ## v1.8.4
5
52
 
6
53
  [compare changes](https://github.com/rrd108/nuxt-users/compare/v1.8.3...v1.8.4)
package/dist/cli.mjs CHANGED
@@ -4,7 +4,8 @@ import { defaultOptions } from './module.mjs';
4
4
  import { loadNuxt } from '@nuxt/kit';
5
5
  import { defu } from 'defu';
6
6
  import { runMigrations, createUser as createUser$1, createUsersTable as createUsersTable$1, createPersonalAccessTokensTable as createPersonalAccessTokensTable$1, createPasswordResetTokensTable as createPasswordResetTokensTable$1, createMigrationsTable as createMigrationsTable$1 } from '../dist/runtime/server/utils/index.js';
7
- import { getPasswordValidationOptions, validatePassword } from './utils.mjs';
7
+ import { g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.BnOQs2LI.mjs';
8
+ import '../dist/runtime/server/utils/user.js';
8
9
 
9
10
  const getOptionsFromEnv = () => {
10
11
  const connectorName = process.env.DB_CONNECTOR || "sqlite";
@@ -247,7 +248,7 @@ const main = defineCommand({
247
248
  meta: {
248
249
  name: "nuxt-users",
249
250
  description: "CLI for Nuxt Users Module - Manage users, migrations, and database operations",
250
- version: "1.8.3"
251
+ version: "1.8.5"
251
252
  },
252
253
  subCommands: {
253
254
  migrate,
package/dist/module.d.mts CHANGED
@@ -1,139 +1,7 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { R as RuntimeModuleOptions, M as ModuleOptions } from './shared/nuxt-users.CXezgQLO.mjs';
2
3
  export * from '../dist/runtime/server/utils/index.js';
3
4
 
4
- type DatabaseType = 'sqlite' | 'mysql' | 'postgresql';
5
- type DatabaseConfig = {
6
- path?: string;
7
- host?: string;
8
- port?: number;
9
- user?: string;
10
- password?: string;
11
- database?: string;
12
- };
13
- interface RuntimeModuleOptions {
14
- connector?: {
15
- name: DatabaseType;
16
- options: DatabaseConfig;
17
- };
18
- tables?: {
19
- migrations?: string;
20
- users?: string;
21
- personalAccessTokens?: string;
22
- passwordResetTokens?: string;
23
- };
24
- /**
25
- * Mailer configuration options for sending emails (e.g., password resets)
26
- * Uses nodemailer
27
- */
28
- mailer?: MailerOptions;
29
- /**
30
- * Base URL for password reset links
31
- * @default 'http://localhost:3000' // Example, will be set in module defaults
32
- */
33
- passwordResetBaseUrl?: string;
34
- /**
35
- * Skip database checks during module setup to prevent hanging
36
- * @default false
37
- */
38
- auth?: {
39
- /**
40
- * Whitelisted routes that do not require authentication
41
- * @default ['/login']
42
- * @example ['/login', '/register']
43
- */
44
- whitelist?: string[];
45
- /**
46
- * Token expiration time in minutes
47
- * @default 1440
48
- */
49
- tokenExpiration?: number;
50
- /**
51
- * Role-based permissions configuration
52
- * @default {}
53
- * @example {
54
- * admin: ['*'], // admin can access everything
55
- * user: ['/profile', '/api/user/profile'],
56
- * moderator: ['/admin/*', '/api/admin/*']
57
- * }
58
- */
59
- permissions?: Record<string, string[]>;
60
- };
61
- /**
62
- * Password validation configuration
63
- */
64
- passwordValidation?: {
65
- /**
66
- * Minimum password length
67
- * @default 8
68
- */
69
- minLength?: number;
70
- /**
71
- * Require uppercase letters
72
- * @default true
73
- */
74
- requireUppercase?: boolean;
75
- /**
76
- * Require lowercase letters
77
- * @default true
78
- */
79
- requireLowercase?: boolean;
80
- /**
81
- * Require numbers
82
- * @default true
83
- */
84
- requireNumbers?: boolean;
85
- /**
86
- * Require special characters
87
- * @default true
88
- */
89
- requireSpecialChars?: boolean;
90
- /**
91
- * Prevent common passwords
92
- * @default true
93
- */
94
- preventCommonPasswords?: boolean;
95
- };
96
- }
97
- interface ModuleOptions {
98
- connector: {
99
- name: DatabaseType;
100
- options: DatabaseConfig;
101
- };
102
- tables: {
103
- migrations: string;
104
- users: string;
105
- personalAccessTokens: string;
106
- passwordResetTokens: string;
107
- };
108
- mailer?: MailerOptions;
109
- passwordResetBaseUrl?: string;
110
- auth: {
111
- whitelist: string[];
112
- tokenExpiration: number;
113
- permissions: Record<string, string[]>;
114
- };
115
- passwordValidation: {
116
- minLength: number;
117
- requireUppercase: boolean;
118
- requireLowercase: boolean;
119
- requireNumbers: boolean;
120
- requireSpecialChars: boolean;
121
- preventCommonPasswords: boolean;
122
- };
123
- }
124
- interface MailerOptions {
125
- host: string;
126
- port: number;
127
- secure?: boolean;
128
- auth: {
129
- user: string;
130
- pass: string;
131
- };
132
- defaults?: {
133
- from: string;
134
- };
135
- }
136
-
137
5
  declare const defaultOptions: ModuleOptions;
138
6
  declare const _default: _nuxt_schema.NuxtModule<RuntimeModuleOptions, RuntimeModuleOptions, false>;
139
7
 
package/dist/module.d.ts CHANGED
@@ -1,139 +1,7 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { R as RuntimeModuleOptions, M as ModuleOptions } from './shared/nuxt-users.CXezgQLO.js';
2
3
  export * from '../dist/runtime/server/utils/index.js';
3
4
 
4
- type DatabaseType = 'sqlite' | 'mysql' | 'postgresql';
5
- type DatabaseConfig = {
6
- path?: string;
7
- host?: string;
8
- port?: number;
9
- user?: string;
10
- password?: string;
11
- database?: string;
12
- };
13
- interface RuntimeModuleOptions {
14
- connector?: {
15
- name: DatabaseType;
16
- options: DatabaseConfig;
17
- };
18
- tables?: {
19
- migrations?: string;
20
- users?: string;
21
- personalAccessTokens?: string;
22
- passwordResetTokens?: string;
23
- };
24
- /**
25
- * Mailer configuration options for sending emails (e.g., password resets)
26
- * Uses nodemailer
27
- */
28
- mailer?: MailerOptions;
29
- /**
30
- * Base URL for password reset links
31
- * @default 'http://localhost:3000' // Example, will be set in module defaults
32
- */
33
- passwordResetBaseUrl?: string;
34
- /**
35
- * Skip database checks during module setup to prevent hanging
36
- * @default false
37
- */
38
- auth?: {
39
- /**
40
- * Whitelisted routes that do not require authentication
41
- * @default ['/login']
42
- * @example ['/login', '/register']
43
- */
44
- whitelist?: string[];
45
- /**
46
- * Token expiration time in minutes
47
- * @default 1440
48
- */
49
- tokenExpiration?: number;
50
- /**
51
- * Role-based permissions configuration
52
- * @default {}
53
- * @example {
54
- * admin: ['*'], // admin can access everything
55
- * user: ['/profile', '/api/user/profile'],
56
- * moderator: ['/admin/*', '/api/admin/*']
57
- * }
58
- */
59
- permissions?: Record<string, string[]>;
60
- };
61
- /**
62
- * Password validation configuration
63
- */
64
- passwordValidation?: {
65
- /**
66
- * Minimum password length
67
- * @default 8
68
- */
69
- minLength?: number;
70
- /**
71
- * Require uppercase letters
72
- * @default true
73
- */
74
- requireUppercase?: boolean;
75
- /**
76
- * Require lowercase letters
77
- * @default true
78
- */
79
- requireLowercase?: boolean;
80
- /**
81
- * Require numbers
82
- * @default true
83
- */
84
- requireNumbers?: boolean;
85
- /**
86
- * Require special characters
87
- * @default true
88
- */
89
- requireSpecialChars?: boolean;
90
- /**
91
- * Prevent common passwords
92
- * @default true
93
- */
94
- preventCommonPasswords?: boolean;
95
- };
96
- }
97
- interface ModuleOptions {
98
- connector: {
99
- name: DatabaseType;
100
- options: DatabaseConfig;
101
- };
102
- tables: {
103
- migrations: string;
104
- users: string;
105
- personalAccessTokens: string;
106
- passwordResetTokens: string;
107
- };
108
- mailer?: MailerOptions;
109
- passwordResetBaseUrl?: string;
110
- auth: {
111
- whitelist: string[];
112
- tokenExpiration: number;
113
- permissions: Record<string, string[]>;
114
- };
115
- passwordValidation: {
116
- minLength: number;
117
- requireUppercase: boolean;
118
- requireLowercase: boolean;
119
- requireNumbers: boolean;
120
- requireSpecialChars: boolean;
121
- preventCommonPasswords: boolean;
122
- };
123
- }
124
- interface MailerOptions {
125
- host: string;
126
- port: number;
127
- secure?: boolean;
128
- auth: {
129
- user: string;
130
- pass: string;
131
- };
132
- defaults?: {
133
- from: string;
134
- };
135
- }
136
-
137
5
  declare const defaultOptions: ModuleOptions;
138
6
  declare const _default: _nuxt_schema.NuxtModule<RuntimeModuleOptions, RuntimeModuleOptions, false>;
139
7
 
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-users",
3
3
  "configKey": "nuxtUsers",
4
- "version": "1.8.4",
4
+ "version": "1.9.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
7
7
  "unbuild": "3.6.0"
package/dist/module.mjs CHANGED
@@ -128,6 +128,29 @@ const module = defineNuxtModule({
128
128
  });
129
129
  nuxt.hook("nitro:config", async (nitroConfig) => {
130
130
  nitroConfig.experimental = nitroConfig.experimental || {};
131
+ nitroConfig.experimental.database = true;
132
+ nitroConfig.database = nitroConfig.database || {};
133
+ if (!nitroConfig.database.default) {
134
+ const connectorOptions = { ...runtimeConfigOptions.connector.options };
135
+ let nitroConnector;
136
+ switch (runtimeConfigOptions.connector.name) {
137
+ case "sqlite":
138
+ nitroConnector = "better-sqlite3";
139
+ break;
140
+ case "mysql":
141
+ nitroConnector = "mysql2";
142
+ break;
143
+ case "postgresql":
144
+ nitroConnector = "postgresql";
145
+ break;
146
+ default:
147
+ nitroConnector = "better-sqlite3";
148
+ }
149
+ nitroConfig.database.default = {
150
+ connector: nitroConnector,
151
+ options: connectorOptions
152
+ };
153
+ }
131
154
  nitroConfig.experimental.tasks = true;
132
155
  nitroConfig.scanDirs = nitroConfig.scanDirs || [];
133
156
  nitroConfig.scanDirs.push(resolver.resolve("./runtime/server/tasks"));
@@ -0,0 +1,15 @@
1
+ export declare const useNuxtUsersDatabase: () => Promise<{
2
+ database: import("db0").Database<import("db0").Connector<Database.Database>>;
3
+ options: {
4
+ tables: {
5
+ migrations: string;
6
+ users: string;
7
+ personalAccessTokens: string;
8
+ passwordResetTokens: string;
9
+ };
10
+ connector: {
11
+ name: import("../../types.js").DatabaseType;
12
+ options: import("../../types.js").DatabaseConfig;
13
+ };
14
+ };
15
+ }>;
@@ -0,0 +1,14 @@
1
+ export const useNuxtUsersDatabase = async () => {
2
+ const { useRuntimeConfig } = await import("#imports");
3
+ const { useDb } = await import("../server/utils/db.js");
4
+ const { nuxtUsers } = useRuntimeConfig();
5
+ const options = nuxtUsers;
6
+ const database = await useDb(options);
7
+ return {
8
+ database,
9
+ options: {
10
+ tables: options.tables,
11
+ connector: options.connector
12
+ }
13
+ };
14
+ };
@@ -1,7 +1,8 @@
1
1
  export { useDb, checkTableExists } from './db.js';
2
- export { createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens } from './user.js';
2
+ export { createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens, getLastLoginTime } from './user.js';
3
3
  export { createUsersTable } from './create-users-table.js';
4
4
  export { createPersonalAccessTokensTable } from './create-personal-access-tokens-table.js';
5
5
  export { createPasswordResetTokensTable } from './create-password-reset-tokens-table.js';
6
6
  export { createMigrationsTable } from './create-migrations-table.js';
7
7
  export { runMigrations } from './migrate.js';
8
+ export { useNuxtUsersDatabase } from '../../composables/useNuxtUsersDatabase.js';
@@ -1,7 +1,8 @@
1
1
  export { useDb, checkTableExists } from "./db.js";
2
- export { createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens } from "./user.js";
2
+ export { createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens, getLastLoginTime } from "./user.js";
3
3
  export { createUsersTable } from "./create-users-table.js";
4
4
  export { createPersonalAccessTokensTable } from "./create-personal-access-tokens-table.js";
5
5
  export { createPasswordResetTokensTable } from "./create-password-reset-tokens-table.js";
6
6
  export { createMigrationsTable } from "./create-migrations-table.js";
7
7
  export { runMigrations } from "./migrate.js";
8
+ export { useNuxtUsersDatabase } from "../../composables/useNuxtUsersDatabase.js";
@@ -44,4 +44,9 @@ export declare const cleanupPersonalAccessTokens: (options: ModuleOptions, inclu
44
44
  * Revokes all tokens for a specific user.
45
45
  */
46
46
  export declare const revokeUserTokens: (userId: number, options: ModuleOptions) => Promise<void>;
47
+ /**
48
+ * Gets the last login time for a user based on their most recent personal access token creation.
49
+ * This represents when the user last successfully logged in.
50
+ */
51
+ export declare const getLastLoginTime: (userId: number, options: ModuleOptions) => Promise<string | null>;
47
52
  export {};
@@ -141,3 +141,19 @@ export const revokeUserTokens = async (userId, options) => {
141
141
  `;
142
142
  console.log(`[Nuxt Users] All tokens revoked for user ID: ${userId}`);
143
143
  };
144
+ export const getLastLoginTime = async (userId, options) => {
145
+ const db = await useDb(options);
146
+ const personalAccessTokensTable = options.tables.personalAccessTokens;
147
+ const result = await db.sql`
148
+ SELECT created_at
149
+ FROM {${personalAccessTokensTable}}
150
+ WHERE tokenable_id = ${userId} AND tokenable_type = 'user'
151
+ ORDER BY created_at DESC
152
+ LIMIT 1
153
+ `;
154
+ if (result.rows.length === 0) {
155
+ return null;
156
+ }
157
+ const lastLogin = result.rows[0].created_at;
158
+ return lastLogin instanceof Date ? lastLogin.toISOString() : lastLogin;
159
+ };
@@ -0,0 +1,162 @@
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 as a, getPasswordStrengthText as b, getPasswordValidationOptions as g, validatePassword as v };
@@ -0,0 +1,167 @@
1
+ type DatabaseType = 'sqlite' | 'mysql' | 'postgresql';
2
+ type DatabaseConfig = {
3
+ path?: string;
4
+ host?: string;
5
+ port?: number;
6
+ user?: string;
7
+ password?: string;
8
+ database?: string;
9
+ };
10
+ interface RuntimeModuleOptions {
11
+ connector?: {
12
+ name: DatabaseType;
13
+ options: DatabaseConfig;
14
+ };
15
+ tables?: {
16
+ migrations?: string;
17
+ users?: string;
18
+ personalAccessTokens?: string;
19
+ passwordResetTokens?: string;
20
+ };
21
+ /**
22
+ * Mailer configuration options for sending emails (e.g., password resets)
23
+ * Uses nodemailer
24
+ */
25
+ mailer?: MailerOptions;
26
+ /**
27
+ * Base URL for password reset links
28
+ * @default 'http://localhost:3000' // Example, will be set in module defaults
29
+ */
30
+ passwordResetBaseUrl?: string;
31
+ /**
32
+ * Skip database checks during module setup to prevent hanging
33
+ * @default false
34
+ */
35
+ auth?: {
36
+ /**
37
+ * Whitelisted routes that do not require authentication
38
+ * @default ['/login']
39
+ * @example ['/login', '/register']
40
+ */
41
+ whitelist?: string[];
42
+ /**
43
+ * Token expiration time in minutes
44
+ * @default 1440
45
+ */
46
+ tokenExpiration?: number;
47
+ /**
48
+ * Role-based permissions configuration
49
+ * @default {}
50
+ * @example {
51
+ * admin: ['*'], // admin can access everything
52
+ * user: ['/profile', '/api/user/profile'],
53
+ * moderator: ['/admin/*', '/api/admin/*']
54
+ * }
55
+ */
56
+ permissions?: Record<string, string[]>;
57
+ };
58
+ /**
59
+ * Password validation configuration
60
+ */
61
+ passwordValidation?: {
62
+ /**
63
+ * Minimum password length
64
+ * @default 8
65
+ */
66
+ minLength?: number;
67
+ /**
68
+ * Require uppercase letters
69
+ * @default true
70
+ */
71
+ requireUppercase?: boolean;
72
+ /**
73
+ * Require lowercase letters
74
+ * @default true
75
+ */
76
+ requireLowercase?: boolean;
77
+ /**
78
+ * Require numbers
79
+ * @default true
80
+ */
81
+ requireNumbers?: boolean;
82
+ /**
83
+ * Require special characters
84
+ * @default true
85
+ */
86
+ requireSpecialChars?: boolean;
87
+ /**
88
+ * Prevent common passwords
89
+ * @default true
90
+ */
91
+ preventCommonPasswords?: boolean;
92
+ };
93
+ }
94
+ interface ModuleOptions {
95
+ connector: {
96
+ name: DatabaseType;
97
+ options: DatabaseConfig;
98
+ };
99
+ tables: {
100
+ migrations: string;
101
+ users: string;
102
+ personalAccessTokens: string;
103
+ passwordResetTokens: string;
104
+ };
105
+ mailer?: MailerOptions;
106
+ passwordResetBaseUrl?: string;
107
+ auth: {
108
+ whitelist: string[];
109
+ tokenExpiration: number;
110
+ permissions: Record<string, string[]>;
111
+ };
112
+ passwordValidation: {
113
+ minLength: number;
114
+ requireUppercase: boolean;
115
+ requireLowercase: boolean;
116
+ requireNumbers: boolean;
117
+ requireSpecialChars: boolean;
118
+ preventCommonPasswords: boolean;
119
+ };
120
+ }
121
+ interface MailerOptions {
122
+ host: string;
123
+ port: number;
124
+ secure?: boolean;
125
+ auth: {
126
+ user: string;
127
+ pass: string;
128
+ };
129
+ defaults?: {
130
+ from: string;
131
+ };
132
+ }
133
+ interface User {
134
+ id: number;
135
+ email: string;
136
+ name: string;
137
+ password: string;
138
+ role: string;
139
+ created_at: string;
140
+ updated_at: string;
141
+ }
142
+ type UserWithoutPassword = Omit<User, 'password'>;
143
+ interface PersonalAccessToken {
144
+ id: number;
145
+ tokenable_type: string;
146
+ tokenable_id: number;
147
+ name: string;
148
+ token: string;
149
+ abilities?: string;
150
+ last_used_at?: string;
151
+ expires_at?: string;
152
+ created_at: string;
153
+ updated_at: string;
154
+ }
155
+ interface PasswordResetToken {
156
+ id: number;
157
+ email: string;
158
+ token: string;
159
+ created_at: string;
160
+ }
161
+ interface LoginFormData {
162
+ email: string;
163
+ password: string;
164
+ rememberMe?: boolean;
165
+ }
166
+
167
+ export type { LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b };
@@ -0,0 +1,167 @@
1
+ type DatabaseType = 'sqlite' | 'mysql' | 'postgresql';
2
+ type DatabaseConfig = {
3
+ path?: string;
4
+ host?: string;
5
+ port?: number;
6
+ user?: string;
7
+ password?: string;
8
+ database?: string;
9
+ };
10
+ interface RuntimeModuleOptions {
11
+ connector?: {
12
+ name: DatabaseType;
13
+ options: DatabaseConfig;
14
+ };
15
+ tables?: {
16
+ migrations?: string;
17
+ users?: string;
18
+ personalAccessTokens?: string;
19
+ passwordResetTokens?: string;
20
+ };
21
+ /**
22
+ * Mailer configuration options for sending emails (e.g., password resets)
23
+ * Uses nodemailer
24
+ */
25
+ mailer?: MailerOptions;
26
+ /**
27
+ * Base URL for password reset links
28
+ * @default 'http://localhost:3000' // Example, will be set in module defaults
29
+ */
30
+ passwordResetBaseUrl?: string;
31
+ /**
32
+ * Skip database checks during module setup to prevent hanging
33
+ * @default false
34
+ */
35
+ auth?: {
36
+ /**
37
+ * Whitelisted routes that do not require authentication
38
+ * @default ['/login']
39
+ * @example ['/login', '/register']
40
+ */
41
+ whitelist?: string[];
42
+ /**
43
+ * Token expiration time in minutes
44
+ * @default 1440
45
+ */
46
+ tokenExpiration?: number;
47
+ /**
48
+ * Role-based permissions configuration
49
+ * @default {}
50
+ * @example {
51
+ * admin: ['*'], // admin can access everything
52
+ * user: ['/profile', '/api/user/profile'],
53
+ * moderator: ['/admin/*', '/api/admin/*']
54
+ * }
55
+ */
56
+ permissions?: Record<string, string[]>;
57
+ };
58
+ /**
59
+ * Password validation configuration
60
+ */
61
+ passwordValidation?: {
62
+ /**
63
+ * Minimum password length
64
+ * @default 8
65
+ */
66
+ minLength?: number;
67
+ /**
68
+ * Require uppercase letters
69
+ * @default true
70
+ */
71
+ requireUppercase?: boolean;
72
+ /**
73
+ * Require lowercase letters
74
+ * @default true
75
+ */
76
+ requireLowercase?: boolean;
77
+ /**
78
+ * Require numbers
79
+ * @default true
80
+ */
81
+ requireNumbers?: boolean;
82
+ /**
83
+ * Require special characters
84
+ * @default true
85
+ */
86
+ requireSpecialChars?: boolean;
87
+ /**
88
+ * Prevent common passwords
89
+ * @default true
90
+ */
91
+ preventCommonPasswords?: boolean;
92
+ };
93
+ }
94
+ interface ModuleOptions {
95
+ connector: {
96
+ name: DatabaseType;
97
+ options: DatabaseConfig;
98
+ };
99
+ tables: {
100
+ migrations: string;
101
+ users: string;
102
+ personalAccessTokens: string;
103
+ passwordResetTokens: string;
104
+ };
105
+ mailer?: MailerOptions;
106
+ passwordResetBaseUrl?: string;
107
+ auth: {
108
+ whitelist: string[];
109
+ tokenExpiration: number;
110
+ permissions: Record<string, string[]>;
111
+ };
112
+ passwordValidation: {
113
+ minLength: number;
114
+ requireUppercase: boolean;
115
+ requireLowercase: boolean;
116
+ requireNumbers: boolean;
117
+ requireSpecialChars: boolean;
118
+ preventCommonPasswords: boolean;
119
+ };
120
+ }
121
+ interface MailerOptions {
122
+ host: string;
123
+ port: number;
124
+ secure?: boolean;
125
+ auth: {
126
+ user: string;
127
+ pass: string;
128
+ };
129
+ defaults?: {
130
+ from: string;
131
+ };
132
+ }
133
+ interface User {
134
+ id: number;
135
+ email: string;
136
+ name: string;
137
+ password: string;
138
+ role: string;
139
+ created_at: string;
140
+ updated_at: string;
141
+ }
142
+ type UserWithoutPassword = Omit<User, 'password'>;
143
+ interface PersonalAccessToken {
144
+ id: number;
145
+ tokenable_type: string;
146
+ tokenable_id: number;
147
+ name: string;
148
+ token: string;
149
+ abilities?: string;
150
+ last_used_at?: string;
151
+ expires_at?: string;
152
+ created_at: string;
153
+ updated_at: string;
154
+ }
155
+ interface PasswordResetToken {
156
+ id: number;
157
+ email: string;
158
+ token: string;
159
+ created_at: string;
160
+ }
161
+ interface LoginFormData {
162
+ email: string;
163
+ password: string;
164
+ rememberMe?: boolean;
165
+ }
166
+
167
+ export type { LoginFormData as L, ModuleOptions as M, PersonalAccessToken as P, RuntimeModuleOptions as R, User as U, UserWithoutPassword as a, PasswordResetToken as b };
package/dist/utils.d.mts CHANGED
@@ -1,3 +1,6 @@
1
+ export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.CXezgQLO.mjs';
2
+ export { findUserByEmail, getCurrentUserFromToken, getLastLoginTime } from '../dist/runtime/server/utils/user.js';
3
+
1
4
  interface PasswordValidationResult {
2
5
  isValid: boolean;
3
6
  errors: string[];
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export { L as LoginFormData, M as ModuleOptions, b as PasswordResetToken, P as PersonalAccessToken, U as User, a as UserWithoutPassword } from './shared/nuxt-users.CXezgQLO.js';
2
+ export { findUserByEmail, getCurrentUserFromToken, getLastLoginTime } from '../dist/runtime/server/utils/user.js';
3
+
1
4
  interface PasswordValidationResult {
2
5
  isValid: boolean;
3
6
  errors: string[];
package/dist/utils.mjs CHANGED
@@ -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 };
1
+ export { a as getPasswordStrengthColor, b as getPasswordStrengthText, g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.BnOQs2LI.mjs';
2
+ export { findUserByEmail, getCurrentUserFromToken, getLastLoginTime } from '../dist/runtime/server/utils/user.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-users",
3
- "version": "1.8.4",
3
+ "version": "1.9.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",