feat: add admin user system with role-based access control

Implement comprehensive admin user system for Kubernetes deployment:

Backend:
- Add isAdmin field to User model for role-based permissions
- Create adminAuth middleware to protect admin-only routes
- Protect 11 routes across rewards, cache, streets, and analytics endpoints
- Update setup-couchdb.js to seed default admin user at deployment

Kubernetes:
- Add ADMIN_EMAIL and ADMIN_PASSWORD to secrets.yaml
- Add ADMIN_EMAIL to configmap.yaml for non-sensitive config
- Create couchdb-init-job.yaml for automated database initialization
- Update secrets.yaml.example with admin user documentation

Frontend:
- Create AdminRoute component for admin-only page protection
- Create comprehensive AdminDashboard with 5 tabs:
  * Overview: Platform statistics and quick actions
  * Users: List, search, manage admin status, delete users
  * Streets: Create, edit, delete streets
  * Rewards: Create, edit, toggle, delete rewards
  * Content: Moderate posts and events
- Add Admin navigation link in Navbar (visible only to admins)
- Integrate admin routes in App.js

Default admin user:
- Email: will@wills-portal.com
- Created automatically by K8s init job at deployment

Routes protected:
- POST/PUT/DELETE /api/rewards (catalog management)
- POST /api/streets (street creation)
- DELETE /api/cache (cache operations)
- GET /api/analytics/* (platform statistics)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
William Valentin
2025-12-06 13:36:15 -08:00
parent 71c1d82e0e
commit fc23f4d098
15 changed files with 1159 additions and 22 deletions

View File

@@ -367,14 +367,87 @@ class CouchDBSetup {
}
}
async run() {
try {
await this.initialize();
await this.createDatabase();
await this.createIndexes();
await this.createSecurityDocument();
await this.seedBadges();
await this.verifySetup();
async seedAdminUser() {
console.log('👤 Seeding admin user...');
const ADMIN_EMAIL = process.env.ADMIN_EMAIL;
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD;
if (!ADMIN_EMAIL || !ADMIN_PASSWORD) {
console.log('⚠️ ADMIN_EMAIL or ADMIN_PASSWORD not set, skipping admin user creation');
return;
}
const db = this.nano.use(COUCHDB_DB_NAME);
// Check if admin user already exists
try {
const existing = await db.find({
selector: { type: 'user', email: ADMIN_EMAIL },
limit: 1
});
if (existing.docs.length > 0) {
const user = existing.docs[0];
if (!user.isAdmin) {
user.isAdmin = true;
user.updatedAt = new Date().toISOString();
await db.insert(user);
console.log('✅ Existing user promoted to admin');
} else {
console.log(' Admin user already exists');
}
return;
}
} catch (error) {
// Continue with creation
}
// Create new admin user with hashed password
const bcrypt = require('bcryptjs');
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(ADMIN_PASSWORD, salt);
const adminUser = {
_id: `user_admin_${Date.now()}`,
type: 'user',
name: 'Administrator',
email: ADMIN_EMAIL,
password: hashedPassword,
isAdmin: true,
isPremium: true,
points: 0,
avatar: null,
profilePicture: null,
bio: 'System Administrator',
location: '',
website: '',
social: { twitter: '', github: '', linkedin: '' },
privacySettings: { profileVisibility: 'private' },
preferences: { emailNotifications: true, pushNotifications: true, theme: 'light' },
adoptedStreets: [],
completedTasks: [],
posts: [],
events: [],
earnedBadges: [],
stats: { streetsAdopted: 0, tasksCompleted: 0, postsCreated: 0, eventsParticipated: 0, badgesEarned: 0 },
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
await db.insert(adminUser);
console.log(`✅ Admin user created: ${ADMIN_EMAIL}`);
}
async run() {
try {
await this.initialize();
await this.createDatabase();
await this.createIndexes();
await this.createSecurityDocument();
await this.seedBadges();
await this.seedAdminUser();
await this.verifySetup();
console.log('\n🎉 CouchDB setup completed successfully!');
console.log(`\n📋 Connection Details:`);