Added comprehensive user management scripts

- Created list-users.js to display all users with roles and details
- Added promote-user-to-admin.js to elevate regular users to admin
- Added demote-admin-to-user.js with safety check for last admin
- All scripts use proper ES modules and dotenv for environment loading
- Scripts validate user existence and current roles before operations
- Added detailed documentation to scripts/README.md
- Includes user-friendly output with emojis and clear status messages
- Tested promotion functionality successfully
- Maintains database integrity with proper error handling
This commit is contained in:
Randall Stillwell 2025-07-24 20:03:31 -05:00
parent 3bb1c9357a
commit be67815cab
6 changed files with 275 additions and 7 deletions

8
package-lock.json generated
View file

@ -11,7 +11,7 @@
"@neondatabase/serverless": "^1.0.1", "@neondatabase/serverless": "^1.0.1",
"@vercel/postgres": "^0.10.0", "@vercel/postgres": "^0.10.0",
"bcryptjs": "^3.0.2", "bcryptjs": "^3.0.2",
"dotenv": "^17.2.0", "dotenv": "^17.2.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"next": "^15.4.2", "next": "^15.4.2",
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",
@ -2574,9 +2574,9 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "17.2.0", "version": "17.2.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz",
"integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=12" "node": ">=12"

View file

@ -16,7 +16,7 @@
"@neondatabase/serverless": "^1.0.1", "@neondatabase/serverless": "^1.0.1",
"@vercel/postgres": "^0.10.0", "@vercel/postgres": "^0.10.0",
"bcryptjs": "^3.0.2", "bcryptjs": "^3.0.2",
"dotenv": "^17.2.0", "dotenv": "^17.2.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"next": "^15.4.2", "next": "^15.4.2",
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",

View file

@ -4,7 +4,9 @@ This directory contains scripts for bulk importing TCG card data into the databa
## Available Scripts ## Available Scripts
### 1. `import-popular-sets.js` - Popular Sets Import ### Card Import Scripts
#### 1. `import-popular-sets.js` - Popular Sets Import
Imports the most popular and recent sets from all three TCGs (Magic, Pokemon, Lorcana). Imports the most popular and recent sets from all three TCGs (Magic, Pokemon, Lorcana).
**Usage:** **Usage:**
@ -19,7 +21,7 @@ npm run import-popular
**Estimated time:** 2-4 hours (depending on API response times) **Estimated time:** 2-4 hours (depending on API response times)
### 2. `bulk-import-all.js` - Complete Import #### 2. `bulk-import-all.js` - Complete Import
Imports ALL available sets from all TCGs (comprehensive import). Imports ALL available sets from all TCGs (comprehensive import).
**Usage:** **Usage:**
@ -34,6 +36,56 @@ npm run import-all
**Estimated time:** 4-8 hours (depending on API response times) **Estimated time:** 4-8 hours (depending on API response times)
### User Management Scripts
#### 3. `list-users.js` - List All Users
Lists all users in the database with their roles and details.
**Usage:**
```bash
node scripts/list-users.js
```
**Output:** Shows user ID, email, role (admin/user), and creation date.
#### 4. `promote-user-to-admin.js` - Promote User to Admin
Promotes a regular user to admin role.
**Usage:**
```bash
node scripts/promote-user-to-admin.js user@example.com
```
**Requirements:** User must be registered first.
#### 5. `demote-admin-to-user.js` - Demote Admin to User
Demotes an admin back to regular user role.
**Usage:**
```bash
node scripts/demote-admin-to-user.js admin@example.com
```
**Safety:** Ensures at least one admin always remains in the system.
### Database Management Scripts
#### 6. `setup-neon-db.js` - Database Setup
Sets up the Neon PostgreSQL database with all required tables and creates the default admin user.
**Usage:**
```bash
node scripts/setup-neon-db.js
```
#### 7. `reset-db.js` - Database Reset
Resets the database by dropping and recreating all tables.
**Usage:**
```bash
node scripts/reset-db.js
```
## How It Works ## How It Works
1. **Sequential Import**: Scripts import sets one by one to avoid overwhelming the APIs 1. **Sequential Import**: Scripts import sets one by one to avoid overwhelming the APIs

89
scripts/demote-admin-to-user.js Executable file
View file

@ -0,0 +1,89 @@
#!/usr/bin/env node
import { config } from 'dotenv';
import { sql } from '@vercel/postgres';
// Load environment variables
config({ path: '.env.local' });
async function demoteAdminToUser(email) {
if (!email) {
console.error('❌ Error: Email is required');
console.log('Usage: node scripts/demote-admin-to-user.js <email>');
console.log('Example: node scripts/demote-admin-to-user.js admin@example.com');
process.exit(1);
}
try {
console.log('🔍 Looking for admin user:', email);
// Check if user exists
const userCheck = await sql`
SELECT id, email, role FROM users WHERE email = ${email}
`;
if (userCheck.rows.length === 0) {
console.error('❌ User not found:', email);
process.exit(1);
}
const user = userCheck.rows[0];
console.log('👤 Found user:', {
id: user.id,
email: user.email,
currentRole: user.role
});
if (user.role === 'user') {
console.log('✅ User is already a regular user!');
process.exit(0);
}
if (user.role !== 'admin') {
console.error('❌ User is not an admin, cannot demote');
process.exit(1);
}
// Check if this is the last admin
const adminCount = await sql`
SELECT COUNT(*) as count FROM users WHERE role = 'admin'
`;
if (parseInt(adminCount.rows[0].count) <= 1) {
console.error('❌ Cannot demote the last admin user!');
console.log('💡 Make sure there is at least one admin before demoting');
process.exit(1);
}
// Demote admin to user
const result = await sql`
UPDATE users
SET role = 'user'
WHERE email = ${email}
RETURNING id, email, role, created_at
`;
if (result.rows.length > 0) {
const updatedUser = result.rows[0];
console.log('✅ Successfully demoted admin to user!');
console.log('👤 Updated user details:', {
id: updatedUser.id,
email: updatedUser.email,
role: updatedUser.role,
created_at: updatedUser.created_at
});
console.log('');
console.log('🔒 The user no longer has access to admin features');
} else {
console.error('❌ Failed to demote user');
}
} catch (error) {
console.error('❌ Database error:', error.message);
process.exit(1);
}
}
// Get email from command line arguments
const email = process.argv[2];
demoteAdminToUser(email);

51
scripts/list-users.js Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env node
import { config } from 'dotenv';
import { sql } from '@vercel/postgres';
// Load environment variables
config({ path: '.env.local' });
async function listUsers() {
try {
console.log('📋 Fetching all users from database...\n');
const result = await sql`
SELECT id, email, role, created_at
FROM users
ORDER BY created_at DESC
`;
if (result.rows.length === 0) {
console.log('👤 No users found in database');
return;
}
console.log(`Found ${result.rows.length} user(s):\n`);
result.rows.forEach((user, index) => {
const roleEmoji = user.role === 'admin' ? '👑' : '👤';
const createdDate = new Date(user.created_at).toLocaleDateString();
console.log(`${index + 1}. ${roleEmoji} ${user.email}`);
console.log(` ID: ${user.id}`);
console.log(` Role: ${user.role}`);
console.log(` Created: ${createdDate}`);
console.log('');
});
const adminCount = result.rows.filter(user => user.role === 'admin').length;
const userCount = result.rows.filter(user => user.role === 'user').length;
console.log('📊 Summary:');
console.log(` 👑 Admins: ${adminCount}`);
console.log(` 👤 Users: ${userCount}`);
console.log(` 📈 Total: ${result.rows.length}`);
} catch (error) {
console.error('❌ Database error:', error.message);
process.exit(1);
}
}
listUsers();

View file

@ -0,0 +1,76 @@
#!/usr/bin/env node
import { config } from 'dotenv';
import { sql } from '@vercel/postgres';
// Load environment variables
config({ path: '.env.local' });
async function promoteUserToAdmin(email) {
if (!email) {
console.error('❌ Error: Email is required');
console.log('Usage: node scripts/promote-user-to-admin.js <email>');
console.log('Example: node scripts/promote-user-to-admin.js user@example.com');
process.exit(1);
}
try {
console.log('🔍 Looking for user:', email);
// Check if user exists
const userCheck = await sql`
SELECT id, email, role FROM users WHERE email = ${email}
`;
if (userCheck.rows.length === 0) {
console.error('❌ User not found:', email);
console.log('💡 Make sure the user has registered first');
process.exit(1);
}
const user = userCheck.rows[0];
console.log('👤 Found user:', {
id: user.id,
email: user.email,
currentRole: user.role
});
if (user.role === 'admin') {
console.log('✅ User is already an admin!');
process.exit(0);
}
// Promote user to admin
const result = await sql`
UPDATE users
SET role = 'admin'
WHERE email = ${email}
RETURNING id, email, role, created_at
`;
if (result.rows.length > 0) {
const updatedUser = result.rows[0];
console.log('🎉 Successfully promoted user to admin!');
console.log('👑 Updated user details:', {
id: updatedUser.id,
email: updatedUser.email,
role: updatedUser.role,
created_at: updatedUser.created_at
});
console.log('');
console.log('🔐 The user can now access admin features at:');
console.log(' • /admin/card-editor');
console.log(' • /admin/card-import');
} else {
console.error('❌ Failed to promote user');
}
} catch (error) {
console.error('❌ Database error:', error.message);
process.exit(1);
}
}
// Get email from command line arguments
const email = process.argv[2];
promoteUserToAdmin(email);