Implement team-based organization for projects with role-based access control: Backend: - Add teams and team_memberships database tables (migrations 009, 009b) - Add Team and TeamMembership ORM models with relationships - Implement TeamAuthorizationService for team-level access control - Add team CRUD, membership, and projects API endpoints - Update project creation to support team assignment Frontend: - Add TeamContext for managing team state with localStorage persistence - Add TeamSelector component for switching between teams - Add TeamsPage, TeamDashboardPage, TeamSettingsPage, TeamMembersPage - Add team API client functions - Update navigation with Teams link Security: - Team role hierarchy: owner > admin > member - Membership checked before system admin fallback - Self-modification prevention for role changes - Email visibility restricted to team admins/owners - Slug validation rejects consecutive hyphens Tests: - Unit tests for TeamAuthorizationService - Integration tests for all team API endpoints
80 lines
2.9 KiB
TypeScript
80 lines
2.9 KiB
TypeScript
import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
|
|
import { AuthProvider, useAuth } from './contexts/AuthContext';
|
|
import { TeamProvider } from './contexts/TeamContext';
|
|
import Layout from './components/Layout';
|
|
import Home from './pages/Home';
|
|
import ProjectPage from './pages/ProjectPage';
|
|
import PackagePage from './pages/PackagePage';
|
|
import Dashboard from './pages/Dashboard';
|
|
import LoginPage from './pages/LoginPage';
|
|
import ChangePasswordPage from './pages/ChangePasswordPage';
|
|
import APIKeysPage from './pages/APIKeysPage';
|
|
import AdminUsersPage from './pages/AdminUsersPage';
|
|
import AdminOIDCPage from './pages/AdminOIDCPage';
|
|
import ProjectSettingsPage from './pages/ProjectSettingsPage';
|
|
import TeamsPage from './pages/TeamsPage';
|
|
import TeamDashboardPage from './pages/TeamDashboardPage';
|
|
import TeamSettingsPage from './pages/TeamSettingsPage';
|
|
import TeamMembersPage from './pages/TeamMembersPage';
|
|
|
|
// Component that checks if user must change password
|
|
function RequirePasswordChange({ children }: { children: React.ReactNode }) {
|
|
const { user, loading } = useAuth();
|
|
const location = useLocation();
|
|
|
|
if (loading) {
|
|
return null;
|
|
}
|
|
|
|
// If user is logged in and must change password, redirect to change password page
|
|
if (user?.must_change_password && location.pathname !== '/change-password') {
|
|
return <Navigate to="/change-password" replace />;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|
|
|
|
function AppRoutes() {
|
|
return (
|
|
<Routes>
|
|
<Route path="/login" element={<LoginPage />} />
|
|
<Route path="/change-password" element={<ChangePasswordPage />} />
|
|
<Route
|
|
path="*"
|
|
element={
|
|
<RequirePasswordChange>
|
|
<Layout>
|
|
<Routes>
|
|
<Route path="/" element={<Home />} />
|
|
<Route path="/dashboard" element={<Dashboard />} />
|
|
<Route path="/settings/api-keys" element={<APIKeysPage />} />
|
|
<Route path="/admin/users" element={<AdminUsersPage />} />
|
|
<Route path="/admin/oidc" element={<AdminOIDCPage />} />
|
|
<Route path="/teams" element={<TeamsPage />} />
|
|
<Route path="/teams/:slug" element={<TeamDashboardPage />} />
|
|
<Route path="/teams/:slug/settings" element={<TeamSettingsPage />} />
|
|
<Route path="/teams/:slug/members" element={<TeamMembersPage />} />
|
|
<Route path="/project/:projectName" element={<ProjectPage />} />
|
|
<Route path="/project/:projectName/settings" element={<ProjectSettingsPage />} />
|
|
<Route path="/project/:projectName/:packageName" element={<PackagePage />} />
|
|
</Routes>
|
|
</Layout>
|
|
</RequirePasswordChange>
|
|
}
|
|
/>
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
return (
|
|
<AuthProvider>
|
|
<TeamProvider>
|
|
<AppRoutes />
|
|
</TeamProvider>
|
|
</AuthProvider>
|
|
);
|
|
}
|
|
|
|
export default App;
|