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

@@ -0,0 +1,33 @@
import React, { useContext } from "react";
import { Navigate } from "react-router-dom";
import { AuthContext } from "../context/AuthContext";
const AdminRoute = ({ children }) => {
const { auth } = useContext(AuthContext);
// Show loading state while checking authentication
if (auth.loading) {
return (
<div className="d-flex justify-content-center align-items-center" style={{ height: "100vh" }}>
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
);
}
// Redirect to login if not authenticated
if (!auth.isAuthenticated) {
return <Navigate to="/login" replace />;
}
// Redirect to home if not admin
if (!auth.user?.isAdmin) {
return <Navigate to="/map" replace />;
}
// Render the protected admin component
return children;
};
export default AdminRoute;