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

View file

@ -16,7 +16,7 @@
"@neondatabase/serverless": "^1.0.1",
"@vercel/postgres": "^0.10.0",
"bcryptjs": "^3.0.2",
"dotenv": "^17.2.0",
"dotenv": "^17.2.1",
"jsonwebtoken": "^9.0.2",
"next": "^15.4.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
### 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).
**Usage:**
@ -19,7 +21,7 @@ npm run import-popular
**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).
**Usage:**
@ -34,6 +36,56 @@ npm run import-all
**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
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);