UNPKG

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
#!/usr/bin/env node 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);