nuxt-users 1.39.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 +8 -0
- package/dist/cli.mjs +47 -21
- package/dist/module.d.mts +2 -2
- package/dist/module.d.ts +2 -2
- package/dist/module.json +4 -1
- package/dist/module.mjs +18 -5
- package/dist/runtime/assets/nuxt-users.css +1 -1
- package/dist/runtime/composables/useAuthentication.js +14 -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/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/index.patch.js +1 -1
- package/dist/runtime/server/api/nuxt-users/session/index.delete.js +1 -1
- package/dist/runtime/server/api/nuxt-users/session/index.post.js +1 -1
- 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 +16 -2
- 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/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/shared/{nuxt-users.FiQjvlZl.d.mts → nuxt-users.XW6DYM7t.d.mts} +6 -0
- package/dist/shared/{nuxt-users.FiQjvlZl.d.ts → nuxt-users.XW6DYM7t.d.ts} +6 -0
- package/dist/utils.d.mts +1 -1
- package/dist/utils.d.ts +1 -1
- package/package.json +8 -5
- package/scripts/test-nuxt3.sh +78 -0
package/README.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
# Nuxt Users
|
|
2
|
+
[](https://context7.com/rrd108/nuxt-users)
|
|
3
|
+
[](https://www.npmjs.com/package/nuxt-users)
|
|
4
|
+
[](https://www.npmjs.com/package/nuxt-users)
|
|
5
|
+
[](https://github.com/rrd108/nuxt-users/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/rrd108/nuxt-users/actions/workflows/ci.yml)
|
|
7
|
+
[](https://nuxt.com)
|
|
8
|
+
[](https://nuxt.com)
|
|
9
|
+
|
|
2
10
|
|
|
3
11
|
A user authentication module for Nuxt 3 and Nuxt 4 with database support for SQLite, MySQL, and PostgreSQL.
|
|
4
12
|
|
package/dist/cli.mjs
CHANGED
|
@@ -4,14 +4,12 @@ import { readFileSync } from 'node:fs';
|
|
|
4
4
|
import { defaultOptions } from './module.mjs';
|
|
5
5
|
import { loadNuxt } from '@nuxt/kit';
|
|
6
6
|
import { defu } from 'defu';
|
|
7
|
-
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 { runMigrations, createUser as createUser$1, createUsersTable as createUsersTable$1, createPersonalAccessTokensTable as createPersonalAccessTokensTable$1, createPasswordResetTokensTable as createPasswordResetTokensTable$1, createMigrationsTable as createMigrationsTable$1, useDb, checkTableExists, getAppliedMigrations } from '../dist/runtime/server/utils/index.js';
|
|
8
8
|
import { g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.DfZ7bc_P.mjs';
|
|
9
9
|
import '../dist/runtime/locales/index.js';
|
|
10
10
|
import '../dist/runtime/utils/locale.js';
|
|
11
11
|
import '../dist/runtime/utils/deep-merge.js';
|
|
12
12
|
import { addActiveToUsers as addActiveToUsers$1 } from '../dist/runtime/server/utils/add-active-to-users.js';
|
|
13
|
-
import { useDb, checkTableExists } from '../dist/runtime/server/utils/db.js';
|
|
14
|
-
import { getAppliedMigrations } from '../dist/runtime/server/utils/migrate.js';
|
|
15
13
|
import '../dist/runtime/server/internal/index.js';
|
|
16
14
|
|
|
17
15
|
const getOptionsFromEnv = () => {
|
|
@@ -55,27 +53,53 @@ const getOptionsFromEnv = () => {
|
|
|
55
53
|
const loadOptions = async () => {
|
|
56
54
|
try {
|
|
57
55
|
console.log("[Nuxt Users] Loading Nuxt project...");
|
|
56
|
+
console.log("[Nuxt Users] \u2139\uFE0F Note: CLI commands read .env files. If you use .env.local in development, export variables first:");
|
|
57
|
+
console.log("[Nuxt Users] set -a; source .env.local; set +a; npx nuxt-users <command>");
|
|
58
|
+
console.log("[Nuxt Users] In production, environment variables are set directly in the deployment environment.");
|
|
58
59
|
const nuxt = await loadNuxt({ cwd: process.cwd(), ready: false });
|
|
59
60
|
const topLevelConfig = nuxt.options.nuxtUsers;
|
|
60
|
-
const
|
|
61
|
-
try {
|
|
62
|
-
const configPath = await import('node:path').then((p) => p.resolve(process.cwd(), "nuxt.config.ts"));
|
|
63
|
-
const config = await import(configPath).then((m) => m.default);
|
|
64
|
-
return config?.runtimeConfig?.nuxtUsers;
|
|
65
|
-
} catch {
|
|
66
|
-
return nuxt.options.runtimeConfig?.nuxtUsers;
|
|
67
|
-
}
|
|
68
|
-
})();
|
|
61
|
+
const processedRuntimeConfig = nuxt.options.runtimeConfig?.nuxtUsers;
|
|
69
62
|
await nuxt.close();
|
|
70
|
-
if (topLevelConfig ||
|
|
63
|
+
if (topLevelConfig || processedRuntimeConfig) {
|
|
71
64
|
console.log("[Nuxt Users] Using configuration from nuxt.config");
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
65
|
+
const { connector: _defaultConnector, ...defaultsWithoutConnector } = defaultOptions;
|
|
66
|
+
const mergedConfig = defu(topLevelConfig, processedRuntimeConfig, defaultsWithoutConnector);
|
|
67
|
+
const configuredConnector = topLevelConfig?.connector || processedRuntimeConfig?.connector;
|
|
68
|
+
mergedConfig.connector = configuredConnector || defaultOptions.connector;
|
|
69
|
+
if (mergedConfig.connector.name === "mysql" || mergedConfig.connector.name === "postgresql") {
|
|
70
|
+
const options = mergedConfig.connector.options;
|
|
71
|
+
const envOptions = {};
|
|
72
|
+
if (!options.user && process.env.DB_USER) {
|
|
73
|
+
envOptions.user = process.env.DB_USER;
|
|
74
|
+
}
|
|
75
|
+
if (!options.password && process.env.DB_PASSWORD) {
|
|
76
|
+
envOptions.password = process.env.DB_PASSWORD;
|
|
77
|
+
}
|
|
78
|
+
if (!options.host && process.env.DB_HOST) {
|
|
79
|
+
envOptions.host = process.env.DB_HOST;
|
|
80
|
+
}
|
|
81
|
+
if (!options.database && process.env.DB_NAME) {
|
|
82
|
+
envOptions.database = process.env.DB_NAME;
|
|
83
|
+
}
|
|
84
|
+
if (!options.port && process.env.DB_PORT) {
|
|
85
|
+
envOptions.port = Number.parseInt(process.env.DB_PORT);
|
|
86
|
+
}
|
|
87
|
+
if (Object.keys(envOptions).length > 0) {
|
|
88
|
+
mergedConfig.connector.options = { ...options, ...envOptions };
|
|
89
|
+
console.log("[Nuxt Users] Filled in missing connector options from environment variables");
|
|
90
|
+
}
|
|
91
|
+
const finalOptions = mergedConfig.connector.options;
|
|
92
|
+
if (!finalOptions.user || !finalOptions.password || !finalOptions.host || !finalOptions.database) {
|
|
93
|
+
console.warn("[Nuxt Users] Warning: MySQL/PostgreSQL connector options are incomplete.");
|
|
94
|
+
console.warn("[Nuxt Users] Required: user, password, host, database");
|
|
95
|
+
console.warn("[Nuxt Users] Found:", {
|
|
96
|
+
user: finalOptions.user || "(missing)",
|
|
97
|
+
password: finalOptions.password ? "***" : "(missing)",
|
|
98
|
+
host: finalOptions.host || "(missing)",
|
|
99
|
+
database: finalOptions.database || "(missing)"
|
|
100
|
+
});
|
|
101
|
+
console.warn("[Nuxt Users] Please set these in nuxt.config.ts or as environment variables (DB_USER, DB_PASSWORD, DB_HOST, DB_NAME)");
|
|
102
|
+
}
|
|
79
103
|
}
|
|
80
104
|
return mergedConfig;
|
|
81
105
|
}
|
|
@@ -329,7 +353,9 @@ const projectInfo = defineCommand({
|
|
|
329
353
|
async run() {
|
|
330
354
|
try {
|
|
331
355
|
console.log("[Nuxt Users] Loading Nuxt project...");
|
|
332
|
-
console.log("[Nuxt Users] \u2139\uFE0F Note:
|
|
356
|
+
console.log("[Nuxt Users] \u2139\uFE0F Note: CLI commands read .env files. If you use .env.local in development, export variables first:");
|
|
357
|
+
console.log("[Nuxt Users] set -a; source .env.local; set +a; npx nuxt-users <command>");
|
|
358
|
+
console.log("[Nuxt Users] In production, environment variables are set directly in the deployment environment.");
|
|
333
359
|
const nuxt = await loadNuxt({ cwd: process.cwd() });
|
|
334
360
|
console.log("[Nuxt Users] \u2705 Nuxt project loaded successfully!");
|
|
335
361
|
console.log("[Nuxt Users] \u{1F4C1} Source directory:", nuxt.options.srcDir);
|
package/dist/module.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { M as ModuleOptions } from './shared/nuxt-users.XW6DYM7t.mjs';
|
|
3
3
|
export * from '../dist/runtime/server/internal/index.js';
|
|
4
4
|
|
|
5
5
|
declare const defaultOptions: ModuleOptions;
|
|
6
|
-
declare const _default: _nuxt_schema.NuxtModule<
|
|
6
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
7
7
|
|
|
8
8
|
export { _default as default, defaultOptions };
|
package/dist/module.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
import {
|
|
2
|
+
import { M as ModuleOptions } from './shared/nuxt-users.XW6DYM7t.js';
|
|
3
3
|
export * from '../dist/runtime/server/internal/index.js';
|
|
4
4
|
|
|
5
5
|
declare const defaultOptions: ModuleOptions;
|
|
6
|
-
declare const _default: _nuxt_schema.NuxtModule<
|
|
6
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
7
7
|
|
|
8
8
|
export { _default as default, defaultOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addServerImportsDir, addServerHandler, addRouteMiddleware, addComponentsDir } from '@nuxt/kit';
|
|
1
|
+
import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addServerImportsDir, addServerScanDir, addServerHandler, addRouteMiddleware, addComponentsDir } from '@nuxt/kit';
|
|
2
2
|
import { defu } from 'defu';
|
|
3
3
|
export * from '../dist/runtime/server/internal/index.js';
|
|
4
4
|
|
|
@@ -55,12 +55,17 @@ const defaultOptions = {
|
|
|
55
55
|
default: "en",
|
|
56
56
|
fallbackLocale: "en",
|
|
57
57
|
texts: {}
|
|
58
|
-
}
|
|
58
|
+
},
|
|
59
|
+
tokenCleanupSchedule: "0 2 * * *"
|
|
60
|
+
// Daily at 2 AM
|
|
59
61
|
};
|
|
60
62
|
const module$1 = defineNuxtModule({
|
|
61
63
|
meta: {
|
|
62
64
|
name: "nuxt-users",
|
|
63
|
-
configKey: "nuxtUsers"
|
|
65
|
+
configKey: "nuxtUsers",
|
|
66
|
+
compatibility: {
|
|
67
|
+
nuxt: ">=3.0.0"
|
|
68
|
+
}
|
|
64
69
|
},
|
|
65
70
|
// Default configuration options of the Nuxt module
|
|
66
71
|
defaults: defaultOptions,
|
|
@@ -163,6 +168,7 @@ const module$1 = defineNuxtModule({
|
|
|
163
168
|
});
|
|
164
169
|
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
165
170
|
addServerImportsDir(resolver.resolve("./runtime/server/composables"));
|
|
171
|
+
addServerScanDir(resolver.resolve("./runtime/server"));
|
|
166
172
|
addServerHandler({
|
|
167
173
|
middleware: true,
|
|
168
174
|
handler: resolver.resolve("./runtime/server/middleware/authorization.server")
|
|
@@ -257,8 +263,15 @@ const module$1 = defineNuxtModule({
|
|
|
257
263
|
nitroConfig.experimental = nitroConfig.experimental || {};
|
|
258
264
|
nitroConfig.experimental.database = true;
|
|
259
265
|
nitroConfig.experimental.tasks = true;
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
const cleanupSchedule = runtimeConfigOptions.tokenCleanupSchedule ?? defaultOptions.tokenCleanupSchedule;
|
|
267
|
+
if (cleanupSchedule) {
|
|
268
|
+
nitroConfig.scheduledTasks = nitroConfig.scheduledTasks || {};
|
|
269
|
+
nitroConfig.scheduledTasks[cleanupSchedule] = [
|
|
270
|
+
...nitroConfig.scheduledTasks[cleanupSchedule] || [],
|
|
271
|
+
"nuxt-users:cleanup-tokens"
|
|
272
|
+
];
|
|
273
|
+
console.log(`[Nuxt Users] \u{1F504} Token cleanup scheduled: ${cleanupSchedule}`);
|
|
274
|
+
}
|
|
262
275
|
nitroConfig.prerender = nitroConfig.prerender || {};
|
|
263
276
|
nitroConfig.prerender.ignore = nitroConfig.prerender.ignore || [];
|
|
264
277
|
const apiBasePath = runtimeConfigOptions.apiBasePath || defaultOptions.apiBasePath;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--nu-color-primary:#3b82f6;--nu-color-primary-dark:#2563eb;--nu-color-primary-light:#60a5fa;--nu-color-white:#fff;--nu-color-gray-50:#f9fafb;--nu-color-gray-500:#6b7280;--nu-color-gray-600:#4b5563;--nu-color-gray-700:#374151;--nu-color-gray-900:#111827;--nu-color-success:#059669;--nu-color-success-light:#f0fdf4;--nu-color-success-border:#9ae6b4;--nu-color-error:#dc2626;--nu-color-error-light:#fef2f2;--nu-color-error-border:#fecaca;--nu-color-error-alt:#db5050;--nu-color-error-dark:#b91c1c;--nu-color-border:#e5e7eb;--nu-color-border-light:#d1d5db;--nu-color-border-dark:#ccc;--nu-color-bg-primary:#fff;--nu-color-bg-secondary:#f9fafb;--nu-color-bg-tertiary:#f3f4f6;--nu-color-text-primary:#000;--nu-color-text-secondary:#6b7280;--nu-color-strength-bg:#e9ecef;--nu-color-validation-passed:#28a745;--nu-color-validation-failed:#dc3545;--nu-color-validation-hint:#6c757d}.dark :root{--nu-color-primary:#60a5fa;--nu-color-primary-dark:#3b82f6;--nu-color-primary-light:#93c5fd;--nu-color-white:#000;--nu-color-gray-50:#111827;--nu-color-gray-500:#9ca3af;--nu-color-gray-600:#d1d5db;--nu-color-gray-700:#e5e7eb;--nu-color-gray-900:#f9fafb;--nu-color-success:#10b981;--nu-color-success-light:#064e3b;--nu-color-success-border:#065f46;--nu-color-error:#ef4444;--nu-color-error-light:#450a0a;--nu-color-error-border:#7f1d1d;--nu-color-error-alt:#f87171;--nu-color-error-dark:#dc2626;--nu-color-border:#374151;--nu-color-border-light:#4b5563;--nu-color-border-dark:#6b7280;--nu-color-bg-primary:#111827;--nu-color-bg-secondary:#1f2937;--nu-color-bg-tertiary:#374151;--nu-color-text-primary:#fff;--nu-color-text-secondary:#9ca3af;--nu-color-strength-bg:#374151;--nu-color-validation-passed:#22c55e;--nu-color-validation-failed:#ef4444;--nu-color-validation-hint:#9ca3af}.n-users-form-group{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1rem;width:100%}.n-users-form-group.n-users-form-group-narrow{width:90%}.n-users-form-group label,.n-users-form-label{color:var(--nu-color-text-primary,#000);display:block;font-size:1rem;font-weight:500;margin-bottom:.5rem}.n-users-form-group input,.n-users-form-group select,.n-users-form-input{border:1px solid var(--nu-color-border,#ccc);border-radius:.25rem;font-size:1rem;padding:.5rem;transition:border-color .2s,box-shadow .2s;width:100%}.n-users-form-group input:focus,.n-users-form-group select:focus,.n-users-form-input:focus{border-color:var(--nu-color-primary);box-shadow:0 0 0 .1rem rgba(59,130,246,.1);outline:none}.n-users-form-group input:disabled,.n-users-form-input:disabled{background-color:var(--nu-color-bg-secondary,#f9fafb);cursor:not-allowed}.n-users-form-group input.error,.n-users-form-input.error{border-color:var(--nu-color-error,#dc3545)}.n-users-form-group input[type=email],.n-users-form-group input[type=password],.n-users-form-group input[type=text]{background-color:var(--nu-color-bg-secondary);border:1px solid var(--nu-color-border-light);border-radius:.5em;box-sizing:border-box;color:var(--nu-color-gray-700);font-size:.875rem;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}.n-users-form-group input[type=email]:focus,.n-users-form-group input[type=password]:focus,.n-users-form-group input[type=text]:focus{background-color:var(--nu-color-bg-primary);border-color:var(--nu-color-primary);box-shadow:0 0 0 3px rgba(59,130,246,.1);outline:none}.n-users-form-group input[type=checkbox]{margin:0;width:auto}.n-users-google-btn{align-items:center;display:inline-flex;gap:8px;justify-content:center}.n-users-google-logo{flex-shrink:0}.n-users-btn,.n-users-cancel-btn,.n-users-delete-btn,.n-users-edit-btn,.n-users-google-btn,.n-users-submit-btn{align-self:flex-start;border:none;border-radius:.25rem;cursor:pointer;display:inline-block;font-size:1rem;font-weight:500;padding:.5rem 1rem;text-decoration:none;transition:background-color .2s,transform .1s}.n-users-btn:hover,.n-users-edit-btn:hover,.n-users-submit-btn:hover{background-color:var(--nu-color-primary-dark,#2563eb);transform:translateY(-1px)}.n-users-btn:disabled,.n-users-submit-btn:disabled{cursor:not-allowed;opacity:.6;transform:none}.n-users-btn-primary,.n-users-edit-btn,.n-users-submit-btn{background-color:var(--nu-color-primary,#3b82f6);color:#fff}.n-users-btn-secondary,.n-users-cancel-btn{background-color:var(--nu-color-gray-500,#6b7280);color:#fff}.n-users-cancel-btn:hover{background-color:var(--nu-color-gray-600,#4b5563)}.n-users-delete-btn{background-color:var(--nu-color-error,#ef4444);color:#fff}.n-users-delete-btn:hover{background-color:var(--nu-color-error-alt,#dc2626)}.n-users-login-section button[type=submit],.n-users-register-section button[type=submit],.n-users-reset-password-container button[type=submit],.n-users-user-form button[type=submit],form.n-users-form button[type=submit]{align-items:center;background-color:var(--nu-color-primary);border:1px solid var(--nu-color-primary);border-radius:.5em;color:var(--nu-color-white);cursor:pointer;display:flex;font-size:.875rem;font-weight:500;gap:.5rem;justify-content:center;min-height:44px;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}.n-users-login-section button[type=submit]:hover:not(:disabled),.n-users-register-section button[type=submit]:hover:not(:disabled),.n-users-reset-password-container button[type=submit]:hover:not(:disabled),.n-users-user-form button[type=submit]:hover:not(:disabled),form.n-users-form button[type=submit]:hover:not(:disabled){background-color:var(--nu-color-primary-dark);border-color:var(--nu-color-primary-dark)}.n-users-login-section button[type=submit]:disabled,.n-users-register-section button[type=submit]:disabled,.n-users-reset-password-container button[type=submit]:disabled,.n-users-user-form button[type=submit]:disabled,form.n-users-form button[type=submit]:disabled{cursor:not-allowed;opacity:.6}.n-users-error-message,.n-users-error-text{background-color:var(--nu-color-error-light,#fef2f2);border:1px solid var(--nu-color-error-border,#fecaca);border-radius:.5rem;color:var(--nu-color-error,#dc3545);font-size:.875rem;margin-top:.5rem;padding:.75rem 1rem}.n-users-success-message{background-color:var(--nu-color-success-light,#f0fff4);border:1px solid var(--nu-color-success-border,#9ae6b4);border-radius:.5rem;color:var(--nu-color-success,#10b981);font-size:.875rem;margin-top:.5rem;padding:.75rem 1rem}.n-users-grid{display:grid;gap:1rem}.n-users-grid-2{grid-template-columns:1fr 5fr}.n-users-grid-auto{grid-template-columns:repeat(auto-fill,minmax(12em,1fr))}.n-users-form-actions{align-items:center;display:flex;gap:1rem;margin-top:1.5rem}.n-users-loading-spinner{animation:n-users-spin 1s linear infinite;border:2px solid var(--nu-color-white,#fff);border-radius:50%;border-top:2px solid transparent;height:16px;width:16px}@keyframes n-users-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.n-users-card{background-color:var(--nu-color-bg-primary,#fff);border:1px solid var(--nu-color-border,#e5e7eb);border-radius:.5rem;padding:1rem}.n-users-section{margin:0 auto;max-width:42em;padding:1em}.n-users-section-header{border-bottom:.1em solid var(--nu-color-border,#e5e7eb);margin-bottom:1rem;padding-bottom:.5rem}.n-users-form-help{color:var(--nu-color-text-secondary,#6b7280);font-size:.75rem;margin-top:.25rem}.n-users-strength-bar{background-color:var(--nu-color-strength-bg,#e9ecef);border-radius:2px;height:4px;margin-bottom:4px;overflow:hidden;width:100%}.n-users-strength-fill{height:100%;transition:width .3s ease,background-color .3s ease}.n-users-strength-text{font-size:.75rem;font-weight:500}.n-users-validation-container{background-color:#f8f9fa;border:1px solid #e9ecef;border-radius:4px;margin-top:12px;padding:12px}.n-users-container-title{color:#495057;font-size:.875rem;font-weight:600;margin-bottom:8px}.n-users-list-unstyled{list-style:none;margin:0;padding-left:0}.n-users-list-item{align-items:center;display:flex;font-size:.875rem;margin-bottom:6px;transition:color .2s ease}.n-users-list-item:last-child{margin-bottom:0}.n-users-icon{font-weight:700;margin-right:8px;min-width:16px;text-align:center}.n-users-text{flex:1}.n-users-list ul{list-style:none}.n-users-login-section form,.n-users-register-section form,.n-users-reset-password-container form,.n-users-user-form form,form.n-users-form{display:flex;flex-direction:column;gap:1.5rem}.n-users-login-section{background:linear-gradient(135deg,var(--nu-color-bg-primary) 0,var(--nu-color-bg-secondary) 100%);border:1px solid var(--nu-color-border);border-radius:.75em;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);max-width:25rem}.n-users-login-header{margin-bottom:1em;text-align:center}.n-users-login-title{color:var(--nu-color-gray-700);font-size:1.875rem;font-weight:700;line-height:1.2;margin:0 0 .5em}.n-users-login-subtitle{color:var(--nu-color-gray-700);font-size:.875rem;line-height:1.5;margin:0}.n-users-remember-me{align-items:center;display:flex;gap:.5rem}.n-users-remember-me label{color:var(--nu-color-gray-700);font-size:.875rem;margin:0}.n-users-login-footer{margin-top:1em;text-align:center}.n-users-forgot-password{margin:0}.n-users-forgot-link{align-items:center;color:var(--nu-color-primary);display:inline-flex;font-size:.875rem;gap:.5rem;text-decoration:none;transition:color .2s ease-in-out}.n-users-forgot-link:hover:not(.loading){color:var(--nu-color-primary-dark);text-decoration:underline}.n-users-forgot-link.loading{cursor:not-allowed;opacity:.6;pointer-events:none}.n-users-password-strength{margin-top:8px}.n-users-validation-rules{margin-top:12px}.n-users-rules-title{margin-bottom:8px}.n-users-rules-list{padding-left:0}.n-users-rule-item{margin-bottom:6px}.n-users-rule-item:last-child{margin-bottom:0}.n-users-rule-passed{color:var(--nu-color-validation-passed,#28a745)}.n-users-rule-failed{color:var(--nu-color-validation-failed,#dc3545)}.n-users-rule-icon{margin-right:8px}.n-users-rule-text{flex:1}.n-users-validation-errors{margin-top:8px}.n-users-error-text:last-child{margin-bottom:0}.n-users-password-hints{margin-top:8px}.n-users-hint-title{margin-bottom:8px}.n-users-hint-list{padding-left:16px}.n-users-hint-item{color:var(--nu-color-validation-hint,#6c757d);margin-bottom:4px}.n-users-hint-item:last-child{margin-bottom:0}.n-users-logout-link{background:var(--nu-color-error);border:2px solid transparent;border-color:var(--nu-color-error-dark);border-radius:.5em;color:#fff;padding:.5em 1em;text-decoration:none}.n-users-logout-link:hover{border-color:var(--nu-color-primary)}.n-users-user-card dd{margin:0}.n-users-user-card-actions{display:flex;gap:.5rem;margin-top:1rem}.n-users-user-form{margin:0 auto;max-width:500px}.n-users-form-group input.error{border-color:#dc3545}.n-users-profile-info{background-color:var(--nu-color-bg-primary);border-top-left-radius:.5em;border-top-right-radius:.5em}.n-users-profile-details{font-size:1rem;list-style:none;margin:0;padding:0}.n-users-reset-password-container{background-color:var(--nu-color-bg-primary);border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}@media (max-width:480px){.n-users-login-title{font-size:1.5rem}}.n-users-list-enter-active,.n-users-list-leave-active,.n-users-list-move{transition:all .3s ease}.n-users-list-enter-from,.n-users-list-leave-to{opacity:0;transform:translateX(2rem)}.n-users-list-leave-active{position:absolute}.n-users-list-move{transition-timing-function:ease}
|
|
1
|
+
:root{--nu-color-primary:#3b82f6;--nu-color-primary-dark:#2563eb;--nu-color-primary-light:#60a5fa;--nu-color-white:#fff;--nu-color-gray-50:#f9fafb;--nu-color-gray-500:#6b7280;--nu-color-gray-600:#4b5563;--nu-color-gray-700:#374151;--nu-color-gray-900:#111827;--nu-color-success:#059669;--nu-color-success-light:#f0fdf4;--nu-color-success-border:#9ae6b4;--nu-color-success-text:#047857;--nu-color-error:#dc2626;--nu-color-error-light:#fef2f2;--nu-color-error-border:#fecaca;--nu-color-error-alt:#db5050;--nu-color-error-dark:#b91c1c;--nu-color-border:#e5e7eb;--nu-color-border-light:#d1d5db;--nu-color-border-dark:#ccc;--nu-color-bg-primary:#fff;--nu-color-bg-secondary:#f9fafb;--nu-color-bg-tertiary:#f3f4f6;--nu-color-text-primary:#000;--nu-color-text-secondary:#6b7280;--nu-color-strength-bg:#e9ecef;--nu-color-validation-passed:#15803d;--nu-color-validation-failed:#dc3545;--nu-color-validation-hint:#6c757d}.dark :root{--nu-color-primary:#60a5fa;--nu-color-primary-dark:#3b82f6;--nu-color-primary-light:#93c5fd;--nu-color-white:#000;--nu-color-gray-50:#111827;--nu-color-gray-500:#9ca3af;--nu-color-gray-600:#d1d5db;--nu-color-gray-700:#e5e7eb;--nu-color-gray-900:#f9fafb;--nu-color-success:#10b981;--nu-color-success-light:#064e3b;--nu-color-success-border:#065f46;--nu-color-success-text:#6ee7b7;--nu-color-error:#ef4444;--nu-color-error-light:#450a0a;--nu-color-error-border:#7f1d1d;--nu-color-error-alt:#f87171;--nu-color-error-dark:#dc2626;--nu-color-border:#374151;--nu-color-border-light:#4b5563;--nu-color-border-dark:#6b7280;--nu-color-bg-primary:#111827;--nu-color-bg-secondary:#1f2937;--nu-color-bg-tertiary:#374151;--nu-color-text-primary:#fff;--nu-color-text-secondary:#9ca3af;--nu-color-strength-bg:#374151;--nu-color-validation-passed:#22c55e;--nu-color-validation-failed:#ef4444;--nu-color-validation-hint:#9ca3af}.n-users-form-group{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1rem;width:100%}.n-users-form-group.n-users-form-group-narrow{width:90%}.n-users-form-group label,.n-users-form-label{color:var(--nu-color-text-primary,#000);display:block;font-size:1rem;font-weight:500;margin-bottom:.5rem}.n-users-form-group input,.n-users-form-group select,.n-users-form-input{border:1px solid var(--nu-color-border,#ccc);border-radius:.25rem;font-size:1rem;padding:.5rem;transition:border-color .2s,box-shadow .2s;width:100%}.n-users-form-group input:focus,.n-users-form-group select:focus,.n-users-form-input:focus{border-color:var(--nu-color-primary);box-shadow:0 0 0 .1rem rgba(59,130,246,.1);outline:none}.n-users-form-group input:disabled,.n-users-form-input:disabled{background-color:var(--nu-color-bg-secondary,#f9fafb);cursor:not-allowed}.n-users-form-group input.error,.n-users-form-input.error{border-color:var(--nu-color-error,#dc3545)}.n-users-form-group input[type=email],.n-users-form-group input[type=password],.n-users-form-group input[type=text]{background-color:var(--nu-color-bg-secondary);border:1px solid var(--nu-color-border-light);border-radius:.5em;box-sizing:border-box;color:var(--nu-color-gray-700);font-size:.875rem;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}.n-users-form-group input[type=email]:focus,.n-users-form-group input[type=password]:focus,.n-users-form-group input[type=text]:focus{background-color:var(--nu-color-bg-primary);border-color:var(--nu-color-primary);box-shadow:0 0 0 3px rgba(59,130,246,.1);outline:none}.n-users-form-group input[type=checkbox]{margin:0;width:auto}.n-users-google-btn{align-items:center;border:none;border-radius:.25rem;cursor:pointer;display:inline-flex;font-size:1rem;font-weight:500;gap:8px;justify-content:center;padding:.5rem 1rem;text-decoration:none;transition:background-color .2s,transform .1s}.n-users-google-logo{flex-shrink:0}.n-users-btn,.n-users-cancel-btn,.n-users-delete-btn,.n-users-edit-btn,.n-users-submit-btn{align-self:flex-start;border:none;border-radius:.25rem;cursor:pointer;display:inline-block;font-size:1rem;font-weight:500;padding:.5rem 1rem;text-decoration:none;transition:background-color .2s,transform .1s}.n-users-btn:hover,.n-users-edit-btn:hover,.n-users-submit-btn:hover{background-color:var(--nu-color-primary-dark,#2563eb);transform:translateY(-1px)}.n-users-btn:disabled,.n-users-submit-btn:disabled{cursor:not-allowed;opacity:.6;transform:none}.n-users-btn-primary,.n-users-edit-btn,.n-users-submit-btn{background-color:var(--nu-color-primary,#3b82f6);color:#fff}.n-users-btn-secondary,.n-users-cancel-btn{background-color:var(--nu-color-gray-600,#4b5563);color:#fff}.n-users-cancel-btn:hover{background-color:var(--nu-color-gray-700,#374151)}.n-users-delete-btn{background-color:var(--nu-color-error-dark,#b91c1c);color:#fff}.n-users-delete-btn:hover{background-color:#991b1b}.n-users-login-section button[type=submit],.n-users-register-section button[type=submit],.n-users-reset-password-container button[type=submit],.n-users-user-form button[type=submit],form.n-users-form button[type=submit]{align-items:center;background-color:var(--nu-color-primary);border:1px solid var(--nu-color-primary);border-radius:.5em;color:var(--nu-color-white);cursor:pointer;display:flex;font-size:.875rem;font-weight:500;gap:.5rem;justify-content:center;min-height:44px;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}.n-users-login-section button[type=submit]:hover:not(:disabled),.n-users-register-section button[type=submit]:hover:not(:disabled),.n-users-reset-password-container button[type=submit]:hover:not(:disabled),.n-users-user-form button[type=submit]:hover:not(:disabled),form.n-users-form button[type=submit]:hover:not(:disabled){background-color:var(--nu-color-primary-dark);border-color:var(--nu-color-primary-dark)}.n-users-login-section button[type=submit]:disabled,.n-users-register-section button[type=submit]:disabled,.n-users-reset-password-container button[type=submit]:disabled,.n-users-user-form button[type=submit]:disabled,form.n-users-form button[type=submit]:disabled{cursor:not-allowed;opacity:.6}.n-users-error-message,.n-users-error-text{background-color:var(--nu-color-error-light,#fef2f2);border:1px solid var(--nu-color-error-border,#fecaca);border-radius:.5rem;color:var(--nu-color-error-dark,#b91c1c);font-size:.875rem;margin-top:.5rem;padding:.75rem 1rem}.n-users-success-message{background-color:var(--nu-color-success-light,#f0fdf4);border:1px solid var(--nu-color-success-border,#9ae6b4);border-radius:.5rem;color:var(--nu-color-success-text,#047857);font-size:.875rem;margin-top:.5rem;padding:.75rem 1rem}.n-users-grid{display:grid;gap:1rem}.n-users-grid-2{grid-template-columns:1fr 5fr}.n-users-grid-auto{grid-template-columns:repeat(auto-fill,minmax(12em,1fr))}.n-users-form-actions{align-items:center;display:flex;gap:1rem;margin-top:1.5rem}.n-users-loading-spinner{animation:n-users-spin 1s linear infinite;border:2px solid var(--nu-color-white,#fff);border-radius:50%;border-top:2px solid transparent;height:16px;width:16px}@keyframes n-users-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.n-users-card{background-color:var(--nu-color-bg-primary,#fff);border:1px solid var(--nu-color-border,#e5e7eb);border-radius:.5rem;padding:1rem}.n-users-section{margin:0 auto;max-width:42em;padding:1em}.n-users-section-header{border-bottom:.1em solid var(--nu-color-border,#e5e7eb);margin-bottom:1rem;padding-bottom:.5rem}.n-users-form-help{color:var(--nu-color-text-secondary,#6b7280);font-size:.75rem;margin-top:.25rem}.n-users-strength-bar{background-color:var(--nu-color-strength-bg,#e9ecef);border-radius:2px;height:4px;margin-bottom:4px;overflow:hidden;width:100%}.n-users-strength-fill{height:100%;transition:width .3s ease,background-color .3s ease}.n-users-strength-text{font-size:.75rem;font-weight:500}.n-users-validation-container{background-color:#f8f9fa;border:1px solid #e9ecef;border-radius:4px;margin-top:12px;padding:12px}.n-users-container-title{color:#495057;font-size:.875rem;font-weight:600;margin-bottom:8px}.n-users-list-unstyled{list-style:none;margin:0;padding-left:0}.n-users-list-item{align-items:center;display:flex;font-size:.875rem;margin-bottom:6px;transition:color .2s ease}.n-users-list-item:last-child{margin-bottom:0}.n-users-icon{font-weight:700;margin-right:8px;min-width:16px;text-align:center}.n-users-text{flex:1}.n-users-list ul{list-style:none}.n-users-login-section form,.n-users-register-section form,.n-users-reset-password-container form,.n-users-user-form form,form.n-users-form{display:flex;flex-direction:column;gap:1.5rem}.n-users-login-section{background:linear-gradient(135deg,var(--nu-color-bg-primary) 0,var(--nu-color-bg-secondary) 100%);border:1px solid var(--nu-color-border);border-radius:.75em;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);max-width:25rem}.n-users-login-header{margin-bottom:1em;text-align:center}.n-users-login-title{color:var(--nu-color-gray-700);font-size:1.875rem;font-weight:700;line-height:1.2;margin:0 0 .5em}.n-users-login-subtitle{color:var(--nu-color-gray-700);font-size:.875rem;line-height:1.5;margin:0}.n-users-remember-me{align-items:center;display:flex;gap:.5rem}.n-users-remember-me label{color:var(--nu-color-gray-700);font-size:.875rem;margin:0}.n-users-login-footer{margin-top:1em;text-align:center}.n-users-forgot-password{margin:0}.n-users-forgot-link{align-items:center;color:var(--nu-color-primary);display:inline-flex;font-size:.875rem;gap:.5rem;text-decoration:none;transition:color .2s ease-in-out}.n-users-forgot-link:hover:not(.loading){color:var(--nu-color-primary-dark);text-decoration:underline}.n-users-forgot-link.loading{cursor:not-allowed;opacity:.6;pointer-events:none}.n-users-password-strength{margin-top:8px}.n-users-validation-rules{margin-top:12px}.n-users-rules-title{margin-bottom:8px}.n-users-rules-list{padding-left:0}.n-users-rule-item{margin-bottom:6px}.n-users-rule-item:last-child{margin-bottom:0}.n-users-rule-passed{color:var(--nu-color-validation-passed,#15803d)}.n-users-rule-failed{color:var(--nu-color-validation-failed,#dc3545)}.n-users-rule-icon{margin-right:8px}.n-users-rule-text{flex:1}.n-users-validation-errors{margin-top:8px}.n-users-error-text:last-child{margin-bottom:0}.n-users-password-hints{margin-top:8px}.n-users-hint-title{margin-bottom:8px}.n-users-hint-list{padding-left:16px}.n-users-hint-item{color:var(--nu-color-validation-hint,#6c757d);margin-bottom:4px}.n-users-hint-item:last-child{margin-bottom:0}.n-users-logout-link{background:var(--nu-color-error-dark,#b91c1c);border:2px solid transparent;border-color:var(--nu-color-error-dark);border-radius:.5em;color:#fff;padding:.5em 1em;text-decoration:none}.n-users-logout-link:hover{border-color:var(--nu-color-primary)}.n-users-user-card dd{margin:0}.n-users-user-card-actions{display:flex;gap:.5rem;margin-top:1rem}.n-users-user-form{margin:0 auto;max-width:500px}.n-users-form-group input.error{border-color:#dc3545}.n-users-profile-info{background-color:var(--nu-color-bg-primary);border-top-left-radius:.5em;border-top-right-radius:.5em}.n-users-profile-details{font-size:1rem;list-style:none;margin:0;padding:0}.n-users-reset-password-container{background-color:var(--nu-color-bg-primary);border-bottom-left-radius:.5em;border-bottom-right-radius:.5em}@media (max-width:480px){.n-users-login-title{font-size:1.5rem}}.n-users-list-enter-active,.n-users-list-leave-active,.n-users-list-move{transition:all .3s ease}.n-users-list-enter-from,.n-users-list-leave-to{opacity:0;transform:translateX(2rem)}.n-users-list-leave-active{position:absolute}.n-users-list-move{transition-timing-function:ease}
|
|
@@ -45,6 +45,11 @@ export const useAuthentication = () => {
|
|
|
45
45
|
lazy: false
|
|
46
46
|
});
|
|
47
47
|
if (error.value) {
|
|
48
|
+
const statusCode = error.value.statusCode || error.value.status;
|
|
49
|
+
if (statusCode === 401) {
|
|
50
|
+
user.value = null;
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
48
53
|
throw new Error(error.value.message || "Failed to fetch user");
|
|
49
54
|
}
|
|
50
55
|
userData = data.value?.user || null;
|
|
@@ -74,6 +79,15 @@ export const useAuthentication = () => {
|
|
|
74
79
|
}
|
|
75
80
|
return userData;
|
|
76
81
|
} catch (error) {
|
|
82
|
+
const statusCode = error?.statusCode || error?.status || error?.response?.status;
|
|
83
|
+
if (statusCode === 401) {
|
|
84
|
+
user.value = null;
|
|
85
|
+
if (import.meta.client) {
|
|
86
|
+
localStorage.removeItem("user");
|
|
87
|
+
sessionStorage.removeItem("user");
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
77
91
|
console.error("Failed to fetch user:", error);
|
|
78
92
|
user.value = null;
|
|
79
93
|
if (import.meta.client) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { deleteUser } from "../../utils/
|
|
3
|
+
import { deleteUser } from "../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
6
6
|
const options = nuxtUsers;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { findUserById } from "../../utils/
|
|
3
|
+
import { findUserById } from "../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
6
6
|
const options = nuxtUsers;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler, readBody } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { updateUser } from "../../utils/
|
|
3
|
+
import { updateUser } from "../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
6
6
|
const options = nuxtUsers;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
getGoogleUserFromCode,
|
|
6
6
|
findOrCreateGoogleUser,
|
|
7
7
|
createAuthTokenForUser
|
|
8
|
-
} from "../../../../utils/
|
|
8
|
+
} from "../../../../utils/index.js";
|
|
9
9
|
export default defineEventHandler(async (event) => {
|
|
10
10
|
const { nuxtUsers } = useRuntimeConfig();
|
|
11
11
|
const options = nuxtUsers;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineEventHandler, sendRedirect, createError } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { createGoogleOAuth2Client, getGoogleAuthUrl } from "../../../../utils/
|
|
3
|
+
import { createGoogleOAuth2Client, getGoogleAuthUrl } from "../../../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
6
6
|
const options = nuxtUsers;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getQuery } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { useDb } from "../../utils/
|
|
4
|
-
import { getLastLoginTime } from "../../utils/user.js";
|
|
3
|
+
import { useDb, getLastLoginTime } from "../../utils/index.js";
|
|
5
4
|
export default defineEventHandler(async (event) => {
|
|
6
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
7
6
|
const options = nuxtUsers;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getQuery } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { useDb } from "../../utils/
|
|
4
|
-
import { getLastLoginTime } from "../../utils/user.js";
|
|
3
|
+
import { useDb, getLastLoginTime } from "../../utils/index.js";
|
|
5
4
|
export default defineEventHandler(async (event) => {
|
|
6
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
7
6
|
const options = nuxtUsers;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler, readBody } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { createUser } from "../../utils/
|
|
3
|
+
import { createUser } from "../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
6
6
|
const options = nuxtUsers;
|
|
@@ -2,21 +2,28 @@ import { createError, defineEventHandler, getCookie } from "h3";
|
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
3
|
import { getCurrentUserFromToken } from "../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const token = getCookie(event, "auth_token");
|
|
8
|
-
if (!token) {
|
|
9
|
-
throw createError({
|
|
10
|
-
statusCode: 401,
|
|
11
|
-
statusMessage: "Unauthorized - No authentication token found"
|
|
12
|
-
});
|
|
5
|
+
if (!event.context) {
|
|
6
|
+
event.context = {};
|
|
13
7
|
}
|
|
14
|
-
|
|
8
|
+
let user = event.context.nuxtUsers?.user;
|
|
15
9
|
if (!user) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
const token = getCookie(event, "auth_token");
|
|
11
|
+
if (!token) {
|
|
12
|
+
throw createError({
|
|
13
|
+
statusCode: 401,
|
|
14
|
+
statusMessage: "Unauthorized - No authentication token found"
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const { nuxtUsers } = useRuntimeConfig();
|
|
18
|
+
const options = nuxtUsers;
|
|
19
|
+
const validatedUser = await getCurrentUserFromToken(token, options);
|
|
20
|
+
if (!validatedUser) {
|
|
21
|
+
throw createError({
|
|
22
|
+
statusCode: 401,
|
|
23
|
+
statusMessage: "Unauthorized - Invalid authentication token"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
user = validatedUser;
|
|
20
27
|
}
|
|
21
28
|
return { user };
|
|
22
29
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler, readBody } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { updateUser } from "../../utils/
|
|
3
|
+
import { updateUser } from "../../utils/index.js";
|
|
4
4
|
export default defineEventHandler(async (event) => {
|
|
5
5
|
const { nuxtUsers } = useRuntimeConfig();
|
|
6
6
|
const options = nuxtUsers;
|
|
@@ -1,7 +1,7 @@
|
|
|
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 "../../../utils/
|
|
4
|
+
import { getCurrentUserFromToken, updateUserPassword } from "../../../utils/index.js";
|
|
5
5
|
import { validatePassword, getPasswordValidationOptions } from "../../../../../utils";
|
|
6
6
|
export default defineEventHandler(async (event) => {
|
|
7
7
|
const { nuxtUsers } = useRuntimeConfig();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineEventHandler, getCookie, setCookie } from "h3";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
import { useDb } from "../../../utils/
|
|
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 "../../../utils/
|
|
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, rememberMe } = body;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
export { useDb, checkTableExists } from '../utils/
|
|
2
|
-
export { createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens, getLastLoginTime } from '../utils/user.js';
|
|
3
|
-
export { createUsersTable } from '../utils/create-users-table.js';
|
|
4
|
-
export { createPersonalAccessTokensTable } from '../utils/create-personal-access-tokens-table.js';
|
|
5
|
-
export { createPasswordResetTokensTable } from '../utils/create-password-reset-tokens-table.js';
|
|
6
|
-
export { createMigrationsTable } from '../utils/create-migrations-table.js';
|
|
7
|
-
export { runMigrations } from '../utils/migrate.js';
|
|
1
|
+
export { useDb, checkTableExists, createUser, findUserByEmail, updateUserPassword, getCurrentUserFromToken, hasAnyUsers, deleteExpiredPersonalAccessTokens, deleteTokensWithoutExpiration, cleanupPersonalAccessTokens, revokeUserTokens, getLastLoginTime, findUserById, updateUser, deleteUser, createUsersTable, createPersonalAccessTokensTable, createPasswordResetTokensTable, createMigrationsTable, runMigrations, getAppliedMigrations } from '../utils/index.js';
|
|
8
2
|
export { useNuxtUsersDatabase } from '../composables/useNuxtUsersDatabase.js';
|
|
9
3
|
export { useServerAuth } from '../composables/useServerAuth.js';
|
|
@@ -1,9 +1,25 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
export {
|
|
2
|
+
useDb,
|
|
3
|
+
checkTableExists,
|
|
4
|
+
createUser,
|
|
5
|
+
findUserByEmail,
|
|
6
|
+
updateUserPassword,
|
|
7
|
+
getCurrentUserFromToken,
|
|
8
|
+
hasAnyUsers,
|
|
9
|
+
deleteExpiredPersonalAccessTokens,
|
|
10
|
+
deleteTokensWithoutExpiration,
|
|
11
|
+
cleanupPersonalAccessTokens,
|
|
12
|
+
revokeUserTokens,
|
|
13
|
+
getLastLoginTime,
|
|
14
|
+
findUserById,
|
|
15
|
+
updateUser,
|
|
16
|
+
deleteUser,
|
|
17
|
+
createUsersTable,
|
|
18
|
+
createPersonalAccessTokensTable,
|
|
19
|
+
createPasswordResetTokensTable,
|
|
20
|
+
createMigrationsTable,
|
|
21
|
+
runMigrations,
|
|
22
|
+
getAppliedMigrations
|
|
23
|
+
} from "../utils/index.js";
|
|
8
24
|
export { useNuxtUsersDatabase } from "../composables/useNuxtUsersDatabase.js";
|
|
9
25
|
export { useServerAuth } from "../composables/useServerAuth.js";
|
|
@@ -28,9 +28,14 @@ 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
|
}
|
|
36
41
|
if (!event.path.startsWith("/api/")) {
|
|
@@ -41,7 +46,11 @@ export default defineEventHandler(async (event) => {
|
|
|
41
46
|
const user = await getCurrentUserFromToken(token, options);
|
|
42
47
|
if (!user) {
|
|
43
48
|
if (event.path.startsWith("/api/")) {
|
|
44
|
-
|
|
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
|
+
}
|
|
45
54
|
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
46
55
|
}
|
|
47
56
|
if (!event.path.startsWith("/api/")) {
|
|
@@ -49,6 +58,11 @@ export default defineEventHandler(async (event) => {
|
|
|
49
58
|
return;
|
|
50
59
|
}
|
|
51
60
|
}
|
|
61
|
+
if (!event.context) {
|
|
62
|
+
event.context = {};
|
|
63
|
+
}
|
|
64
|
+
event.context.nuxtUsers = event.context.nuxtUsers || {};
|
|
65
|
+
event.context.nuxtUsers.user = user;
|
|
52
66
|
if (event.path === `${base}/me`) {
|
|
53
67
|
console.debug(`[Nuxt Users] authorization: Auto-whitelisted /me endpoint for authenticated user ${user.id}`);
|
|
54
68
|
return;
|
|
@@ -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
|
+
};
|
|
@@ -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) => {
|
|
@@ -229,6 +229,12 @@ interface ModuleOptions {
|
|
|
229
229
|
fallbackLocale?: string;
|
|
230
230
|
texts?: Record<string, LocaleMessages>;
|
|
231
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;
|
|
232
238
|
}
|
|
233
239
|
interface MailerOptions {
|
|
234
240
|
host: string;
|
|
@@ -229,6 +229,12 @@ interface ModuleOptions {
|
|
|
229
229
|
fallbackLocale?: string;
|
|
230
230
|
texts?: Record<string, LocaleMessages>;
|
|
231
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;
|
|
232
238
|
}
|
|
233
239
|
interface MailerOptions {
|
|
234
240
|
host: string;
|
package/dist/utils.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
2
2
|
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
3
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.
|
|
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';
|
|
5
5
|
|
|
6
6
|
interface PasswordValidationResult {
|
|
7
7
|
isValid: boolean;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { defaultLocaleMessages, en, hu, huFormal } from '../dist/runtime/locales/index.js';
|
|
2
2
|
export { getNestedValue, getTranslation } from '../dist/runtime/utils/locale.js';
|
|
3
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.
|
|
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';
|
|
5
5
|
|
|
6
6
|
interface PasswordValidationResult {
|
|
7
7
|
isValid: boolean;
|
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",
|
|
@@ -64,27 +64,30 @@
|
|
|
64
64
|
"postbuild": "node scripts/post-build.js",
|
|
65
65
|
"check:translations": "tsx scripts/check-translations.ts",
|
|
66
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)",
|
|
67
68
|
"prerelease": "node -v | grep -q 'v22' || (echo 'Please use Node.js v22 for deployment' && exit 1)",
|
|
68
|
-
"release": "yarn lint && yarn check:translations && yarn check:locale-consistency && 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",
|
|
69
70
|
"lint": "eslint .",
|
|
70
71
|
"lint:fix": "eslint . --fix",
|
|
71
72
|
"pretest": "node -v | grep -q 'v22' || (echo 'Please use Node.js v22 for testing' && exit 1)",
|
|
72
73
|
"test:quick": "yarn test:unit && yarn test:sqlite",
|
|
73
74
|
"test:release": "yarn test && yarn build && yarn lint:fix",
|
|
74
|
-
"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",
|
|
75
76
|
"test:unit": "./scripts/test-unit.sh",
|
|
76
77
|
"test:integration": "./scripts/test-integration.sh",
|
|
77
78
|
"test:sqlite": "./scripts/test-sqlite.sh",
|
|
78
79
|
"test:mysql": "./scripts/test-mysql.sh",
|
|
79
80
|
"test:postgresql": "./scripts/test-postgresql.sh",
|
|
80
|
-
"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",
|
|
81
84
|
"test:watch": "vitest watch",
|
|
82
85
|
"db:create-users-table": "tsx src/cli/create-users-table.ts",
|
|
83
86
|
"db:create-user": "tsx src/cli/create-user.ts",
|
|
84
87
|
"db:create-personal-access-tokens-table": "tsx src/cli/create-personal-access-tokens-table.ts",
|
|
85
88
|
"db:create-password-reset-tokens-table": "tsx src/cli/create-password-reset-tokens-table.ts",
|
|
86
89
|
"db:create-migrations-table": "tsx src/cli/create-migrations-table.ts",
|
|
87
|
-
"db:migrate": "tsx src/cli/
|
|
90
|
+
"db:migrate": "tsx src/cli/main.ts migrate",
|
|
88
91
|
"db:create-empty-sqlite": "node scripts/create-empty-sqlite.js",
|
|
89
92
|
"docs:dev": "vitepress dev docs",
|
|
90
93
|
"docs:build": "vitepress build docs",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Nuxt 3 Local Test Runner
|
|
4
|
+
# This script temporarily installs Nuxt 3, runs the specified test,
|
|
5
|
+
# and then restores the original Nuxt 4 dependencies.
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
# --- Configuration ---
|
|
10
|
+
NUXT3_VERSION="^3.17.6"
|
|
11
|
+
TEST_TYPE=$1
|
|
12
|
+
|
|
13
|
+
# --- Colors for output ---
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
BLUE='\033[0;34m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
|
|
19
|
+
# --- Helper functions ---
|
|
20
|
+
print_status() {
|
|
21
|
+
echo -e "${BLUE}$1${NC}"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
print_error() {
|
|
25
|
+
echo -e "${RED}❌ $1${NC}"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
print_success() {
|
|
29
|
+
echo -e "${GREEN}✅ $1${NC}"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# --- Cleanup function ---
|
|
33
|
+
cleanup() {
|
|
34
|
+
print_status "🧹 Restoring original dependencies..."
|
|
35
|
+
if [ -f "package.json.bak" ]; then
|
|
36
|
+
mv package.json.bak package.json
|
|
37
|
+
fi
|
|
38
|
+
if [ -f "yarn.lock.bak" ]; then
|
|
39
|
+
mv yarn.lock.bak yarn.lock
|
|
40
|
+
fi
|
|
41
|
+
npx nypm@latest i
|
|
42
|
+
print_success "Dependencies restored to Nuxt 4."
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# --- Main script ---
|
|
46
|
+
|
|
47
|
+
# 1. Validate input
|
|
48
|
+
if [ -z "$TEST_TYPE" ]; then
|
|
49
|
+
print_error "Usage: ./scripts/test-nuxt3.sh [sqlite|mysql|postgresql|unit|integration]"
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# 2. Set up cleanup trap
|
|
54
|
+
trap cleanup EXIT
|
|
55
|
+
|
|
56
|
+
# 3. Backup original package files
|
|
57
|
+
print_status "📦 Backing up package files..."
|
|
58
|
+
cp package.json package.json.bak
|
|
59
|
+
cp yarn.lock yarn.lock.bak
|
|
60
|
+
|
|
61
|
+
# 4. Install Nuxt 3
|
|
62
|
+
print_status "🚀 Installing Nuxt v3 (version: ${NUXT3_VERSION})..."
|
|
63
|
+
npx nypm@latest i nuxt@${NUXT3_VERSION}
|
|
64
|
+
print_success "Nuxt 3 installed."
|
|
65
|
+
|
|
66
|
+
# 5. Run the specified test
|
|
67
|
+
print_status "🧪 Running tests for '$TEST_TYPE' against Nuxt 3..."
|
|
68
|
+
if [ -f "scripts/test-${TEST_TYPE}.sh" ]; then
|
|
69
|
+
"./scripts/test-${TEST_TYPE}.sh"
|
|
70
|
+
else
|
|
71
|
+
print_error "Invalid test type '$TEST_TYPE'. No script found at scripts/test-${TEST_TYPE}.sh"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
print_success "Nuxt 3 tests completed successfully!"
|
|
76
|
+
|
|
77
|
+
# 6. Cleanup will be handled by the trap
|
|
78
|
+
exit 0
|