nuxt-users
Version:
A comprehensive user management module for Nuxt 3 and Nuxt 4 applications with authentication, authorization, database support, and CLI tools
417 lines (405 loc) • 16.3 kB
JavaScript
import { defineCommand, runMain } from 'citty';
import { readFileSync } from 'node:fs';
import { defaultOptions } from './module.mjs';
import { loadNuxt } from '@nuxt/kit';
import { defu } from 'defu';
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';
import { g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.BnOQs2LI.mjs';
import { addActiveToUsers as addActiveToUsers$1 } from '../dist/runtime/server/utils/add-active-to-users.js';
import { useDb, checkTableExists } from '../dist/runtime/server/utils/db.js';
import { getAppliedMigrations } from '../dist/runtime/server/utils/migrate.js';
import '../dist/runtime/server/internal/index.js';
const getOptionsFromEnv = () => {
const connectorName = process.env.DB_CONNECTOR || "sqlite";
let connectorOptions;
switch (connectorName) {
case "sqlite":
connectorOptions = {
path: process.env.DB_PATH || "./data/users.sqlite3"
};
break;
case "mysql":
connectorOptions = {
host: process.env.DB_HOST || "localhost",
port: Number.parseInt(process.env.DB_PORT || "3306"),
user: process.env.DB_USER || "root",
password: process.env.DB_PASSWORD || "",
database: process.env.DB_NAME || "nuxt_users"
};
break;
case "postgresql":
connectorOptions = {
host: process.env.DB_HOST || "localhost",
port: Number.parseInt(process.env.DB_PORT || "5432"),
user: process.env.DB_USER || "postgres",
password: process.env.DB_PASSWORD || "",
database: process.env.DB_NAME || "nuxt_users"
};
break;
default:
throw new Error(`Unsupported database connector: ${connectorName}`);
}
return {
...defaultOptions,
connector: {
name: connectorName,
options: connectorOptions
}
};
};
const loadOptions = async () => {
try {
console.log("[Nuxt Users] Loading Nuxt project...");
const nuxt = await loadNuxt({ cwd: process.cwd(), ready: false });
const topLevelConfig = nuxt.options.nuxtUsers;
const originalRuntimeConfig = await (async () => {
try {
const configPath = await import('node:path').then((p) => p.resolve(process.cwd(), "nuxt.config.ts"));
const config = await import(configPath).then((m) => m.default);
return config?.runtimeConfig?.nuxtUsers;
} catch {
return nuxt.options.runtimeConfig?.nuxtUsers;
}
})();
await nuxt.close();
if (topLevelConfig || originalRuntimeConfig) {
console.log("[Nuxt Users] Using configuration from nuxt.config");
let mergedConfig = defu(topLevelConfig, originalRuntimeConfig, defaultOptions);
const configuredConnector = topLevelConfig?.connector || originalRuntimeConfig?.connector;
if (configuredConnector) {
mergedConfig = {
...mergedConfig,
connector: configuredConnector
};
}
return mergedConfig;
} else {
console.log("[Nuxt Users] No nuxt-users configuration found, using environment variables");
return getOptionsFromEnv();
}
} catch (error) {
console.log("[Nuxt Users] Could not load Nuxt project, using environment variables");
console.error("[Nuxt Users] Error:", error);
return getOptionsFromEnv();
}
};
const migrate = defineCommand({
meta: {
name: "migrate",
description: "Run database migrations for the Nuxt Users module"
},
async run() {
console.log("[Nuxt Users] Starting migration system...");
const options = await loadOptions();
try {
await runMigrations(options);
console.log("[Nuxt Users] Migration completed successfully!");
process.exit(0);
} catch (error) {
console.error("[Nuxt Users] Migration failed:", error);
process.exit(1);
}
}
});
const createUser = defineCommand({
meta: {
name: "create-user",
description: "Create a new user in the database"
},
args: {
email: {
alias: "e",
type: "string",
description: "User email address",
required: true
},
name: {
alias: "n",
type: "string",
description: "User full name",
required: true
},
password: {
alias: "p",
type: "string",
description: "User password",
required: true
},
role: {
alias: "r",
type: "string",
description: 'User role (defaults to "user")',
default: "user"
}
},
async run({ args }) {
const { email, name, password, role } = args;
const options = await loadOptions();
try {
const passwordOptions = getPasswordValidationOptions(options);
const passwordValidation = validatePassword(password, passwordOptions);
if (!passwordValidation.isValid) {
console.error("[Nuxt Users] Password validation failed:");
passwordValidation.errors.forEach((error) => console.error(` - ${error}`));
process.exit(1);
}
const user = await createUser$1({ email, name, password, role }, options);
console.log(`[Nuxt Users] User created successfully: ${user.email} (role: ${user.role})`);
process.exit(0);
} catch (error) {
console.error("[Nuxt Users] Error:", error);
process.exit(1);
}
}
});
const createUsersTable = defineCommand({
meta: {
name: "create-users-table",
description: "Create the users table in the database"
},
async run() {
console.log("[Nuxt Users] Creating users table...");
const options = await loadOptions();
try {
await createUsersTable$1(options);
console.log("[Nuxt Users] Users table created successfully!");
} catch (error) {
console.error("[Nuxt Users] DB:Create Users Table Error:", error);
process.exit(1);
}
}
});
const createPersonalAccessTokensTable = defineCommand({
meta: {
name: "create-personal-access-tokens-table",
description: "Create the personal access tokens table in the database"
},
async run() {
console.log("[Nuxt Users] Creating personal access tokens table...");
const options = await loadOptions();
try {
await createPersonalAccessTokensTable$1(options);
console.log("[Nuxt Users] Personal access tokens table created successfully!");
} catch (error) {
console.error("[Nuxt Users] DB:Create Personal Access Tokens Table Error:", error);
process.exit(1);
}
}
});
const createPasswordResetTokensTable = defineCommand({
meta: {
name: "create-password-reset-tokens-table",
description: "Create the password reset tokens table in the database"
},
async run() {
console.log("[Nuxt Users] Creating password reset tokens table...");
const options = await loadOptions();
try {
await createPasswordResetTokensTable$1(options);
console.log("[Nuxt Users] Password reset tokens table created successfully!");
} catch (error) {
console.error("[Nuxt Users] DB:Create Password Reset Tokens Table Error:", error);
process.exit(1);
}
}
});
const createMigrationsTable = defineCommand({
meta: {
name: "create-migrations-table",
description: "Create the migrations table in the database"
},
async run() {
console.log("[Nuxt Users] Creating migrations table...");
const options = await loadOptions();
try {
await createMigrationsTable$1(options);
console.log("[Nuxt Users] Migrations table created successfully!");
} catch (error) {
console.error("[Nuxt Users] DB:Create Migrations Table Error:", error);
process.exit(1);
}
}
});
const addActiveToUsers = defineCommand({
meta: {
name: "add-active-to-users",
description: "Add the active field to the users table in the database"
},
async run() {
console.log("[Nuxt Users] Adding active field to users table...");
const options = await loadOptions();
try {
await addActiveToUsers$1(options);
console.log("[Nuxt Users] Active field added to users table successfully!");
} catch (error) {
console.error("[Nuxt Users] DB:Add Active to Users Error:", error);
process.exit(1);
}
}
});
const addGoogleOauthFields = defineCommand({
meta: {
name: "add-google-oauth-fields",
description: "Add Google OAuth fields (google_id, profile_picture) to existing users table"
},
async run() {
console.log("[Nuxt Users] Adding Google OAuth fields to users table...");
const options = await loadOptions();
const connectorName = options.connector.name;
const db = await useDb(options);
const tableName = options.tables.users;
console.log(`[Nuxt Users] DB:Migrate ${connectorName} Users Table Adding Google OAuth fields...`);
try {
if (connectorName === "sqlite") {
const tableInfo = await db.sql`PRAGMA table_info(${tableName})`;
const columnNames = tableInfo.rows.map((row) => row.name);
if (!columnNames.includes("google_id")) {
await db.sql`ALTER TABLE {${tableName}} ADD COLUMN google_id TEXT`;
console.log("[Nuxt Users] Added google_id column to SQLite users table \u2705");
}
if (!columnNames.includes("profile_picture")) {
await db.sql`ALTER TABLE {${tableName}} ADD COLUMN profile_picture TEXT`;
console.log("[Nuxt Users] Added profile_picture column to SQLite users table \u2705");
}
if (!columnNames.includes("last_login_at")) {
await db.sql`ALTER TABLE {${tableName}} ADD COLUMN last_login_at DATETIME`;
console.log("[Nuxt Users] Added last_login_at column to SQLite users table \u2705");
}
}
if (connectorName === "mysql") {
const checkColumns = await db.sql`
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = ${tableName}
AND COLUMN_NAME IN ('google_id', 'profile_picture')
`;
const existingColumns = checkColumns.rows.map((row) => row.COLUMN_NAME);
if (!existingColumns.includes("google_id")) {
await db.sql`ALTER TABLE {${tableName}} ADD COLUMN google_id VARCHAR(255) UNIQUE`;
console.log("[Nuxt Users] Added google_id column to MySQL users table \u2705");
}
if (!existingColumns.includes("profile_picture")) {
await db.sql`ALTER TABLE {${tableName}} ADD COLUMN profile_picture TEXT`;
console.log("[Nuxt Users] Added profile_picture column to MySQL users table \u2705");
}
}
if (connectorName === "postgresql") {
const checkColumns = await db.sql`
SELECT column_name
FROM information_schema.columns
WHERE table_name = ${tableName}
AND column_name IN ('google_id', 'profile_picture')
`;
const existingColumns = checkColumns.rows.map((row) => row.column_name);
if (!existingColumns.includes("google_id")) {
await db.sql`ALTER TABLE {${tableName}} ADD COLUMN google_id VARCHAR(255) UNIQUE`;
console.log("[Nuxt Users] Added google_id column to PostgreSQL users table \u2705");
}
if (!existingColumns.includes("profile_picture")) {
await db.sql`ALTER TABLE {${tableName}} ADD COLUMN profile_picture TEXT`;
console.log("[Nuxt Users] Added profile_picture column to PostgreSQL users table \u2705");
}
}
console.log("[Nuxt Users] Google OAuth fields migration completed successfully! \u2705");
} catch (error) {
console.error("[Nuxt Users] DB:Migration Error:", error);
process.exit(1);
}
}
});
const projectInfo = defineCommand({
meta: {
name: "project-info",
description: "Get information about the Nuxt project and module configuration"
},
async run() {
try {
console.log("[Nuxt Users] Loading Nuxt project...");
console.log("[Nuxt Users] \u2139\uFE0F Note: This command only reads .env files. If you use .env.local or other env files, export those variables first.");
const nuxt = await loadNuxt({ cwd: process.cwd() });
console.log("[Nuxt Users] \u2705 Nuxt project loaded successfully!");
console.log("[Nuxt Users] \u{1F4C1} Source directory:", nuxt.options.srcDir);
console.log("[Nuxt Users] \u{1F310} Base URL:", nuxt.options.app?.baseURL || "/");
console.log();
const nuxtUsersConfig = nuxt.options.runtimeConfig?.nuxtUsers;
if (nuxtUsersConfig) {
console.log("[Nuxt Users] \u{1F4BE} Database Configuration:");
console.log("[Nuxt Users] Connector:", nuxtUsersConfig.connector?.name);
if (nuxtUsersConfig.connector?.options) {
const options = nuxtUsersConfig.connector.options;
const connectorName = nuxtUsersConfig.connector.name;
if (connectorName === "sqlite") {
console.log("[Nuxt Users] Path:", options.path || "Not configured");
}
if (connectorName === "mysql" || connectorName === "postgresql") {
console.log("[Nuxt Users] Host:", options.host || "Not configured");
console.log("[Nuxt Users] Port:", options.port || "Not configured");
console.log("[Nuxt Users] Database:", options.database || "Not configured");
console.log("[Nuxt Users] User:", options.user || "Not configured");
}
}
console.log();
console.log("[Nuxt Users] \u{1F4CB} Table Configuration:");
console.log("[Nuxt Users] Users:", nuxtUsersConfig.tables?.users);
console.log("[Nuxt Users] Tokens:", nuxtUsersConfig.tables?.personalAccessTokens);
console.log("[Nuxt Users] Password resets:", nuxtUsersConfig.tables?.passwordResetTokens);
console.log("[Nuxt Users] Migrations:", nuxtUsersConfig.tables?.migrations);
console.log();
try {
const migrationsTableExists = await checkTableExists(nuxtUsersConfig, "migrations");
console.log("[Nuxt Users] \u{1F4CA} Migration Status:");
if (migrationsTableExists) {
const appliedMigrations = await getAppliedMigrations(nuxtUsersConfig);
console.log("[Nuxt Users] Table exists: yes \u2705");
console.log("[Nuxt Users] Applied migrations:", appliedMigrations.length);
if (appliedMigrations.length > 0) {
appliedMigrations.forEach((migration) => {
console.log("[Nuxt Users] -", migration);
});
}
} else {
console.log("[Nuxt Users] Table exists: no \u274C");
console.log('[Nuxt Users] \u27A1\uFE0F Run "npx nuxt-users migrate" to set up the database');
}
} catch (error) {
console.log("[Nuxt Users] Status: \u26A0\uFE0F Error -", error instanceof Error ? error.message : "Unknown error");
}
} else {
console.log("[Nuxt Users] \u26A0\uFE0F Nuxt Users module not configured in runtime config");
}
} catch (error) {
console.error("[Nuxt Users] \u274C Could not load Nuxt project:", error);
process.exit(1);
}
}
});
const getVersion = () => {
try {
const packagePath = new URL("../package.json", import.meta.url);
const packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
return packageJson.version;
} catch {
return "unknown";
}
};
const main = defineCommand({
meta: {
name: "nuxt-users",
description: "CLI for Nuxt Users Module - Manage users, migrations, and database operations",
version: getVersion()
},
subCommands: {
migrate,
"create-user": createUser,
"create-users-table": createUsersTable,
"create-personal-access-tokens-table": createPersonalAccessTokensTable,
"create-password-reset-tokens-table": createPasswordResetTokensTable,
"create-migrations-table": createMigrationsTable,
"add-active-to-users": addActiveToUsers,
"add-google-oauth-fields": addGoogleOauthFields,
"project-info": projectInfo
}
});
runMain(main);